Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Script for OBS Studio to publish session stats to MQTT broker with autodiscovery set up for Home Assistant

OBS MQTT Status to Home Assistant

Setup:

  1. Download script
  2. Install mqtt-wrapper pip install mqtt-wrapper
  3. Open OBS Studio
  4. In OBS Studio add a script (Tools -> Scripts)
  5. Configure script parameters (my base channel is homeassistant and my sensor name is obs, creating the path homeassistant/sensor/obs)

script_parameters

  1. Click the script refresh button

  2. If mqtt autodiscovery is on in home assistant, you should see a sensor called sensor.[MQTT Sensor Name] with attributes containing the stats.

    If you have allowed the MQTT to control OBS, you will see two switches called switch.[MQTT Sensor Name]_record and switch.[MQTT Sensor Name]_stream

    You will also see a switch for each profile named switch.[MQTT Sensor Name]_[OBS Profile Name]_Profile (NOTE: Profiles in OBS will have to contain no spaces)

    If mqtt autodiscovery is not turned on, you will need to add this as your sensor config

    - platform: "mqtt"
      name: "OBS"
      state_topic: "[Your set topic here]/state"
      icon: "mdi:video-wireless"
      force_update: true
      qos: 1
      json_attributes_topic: "[Your set topic here]/attributes"
    

    and if you would like to control via MQTT without autodiscovery you will need to add two mqtt switches for recording and streaming

    - platform: "mqtt"
      name: OBS Record Switch
      state_topic: "[MQTT Base Channel]/switch/[MQTT Sensor Name]/record/state"
      command_topic: "[MQTT Base Channel]/switch/[MQTT Sensor Name]/record/set"
      available_topic: "[MQTT Base Channel]/switch/[MQTT Sensor Name]/record/available"
      payload_on: "ON"
      payload_off: "OFF"
      icon: mdi:record
    - platform: "mqtt"
      name: OBS Stream Switch
      state_topic: "[MQTT Base Channel]/switch/[MQTT Sensor Name]/stream/state"
      command_topic: "[MQTT Base Channel]/switch/[MQTT Sensor Name]/stream/set"
      available_topic: "[MQTT Base Channel]/switch/[MQTT Sensor Name]/stream/available"
      payload_on: "ON"
      payload_off: "OFF"
      icon: mdi:broadcast
    

    If you would like to control profiles without discovery turned on you will need to add switches FOR EACH profile you wish to turn on from Home Assistant

    - platform: "mqtt"
      name: OBS Test Profile
      state_topic: "[MQTT Base Channel]/switch/[OBS Profile Name]/state"
      command_topic: "[MQTT Base Channel]/switch/[OBS Profile Name]/profile/set"
      payload_on: "ON"
      payload_off: "OFF"
      icon: mdi:alpha-p-box
    

Sensor States

  • Recording
  • Streaming
  • Streaming and Recording
  • Stopped (OBS Open)
  • Off (OBS Closed)

NOTE: If you have autodiscovery on when you update the script, make sure to remove the Stream, Record and OBS Sensor configs by doing an empty publish to their respective configs

[Your base channel]/sensor/[Sensor Name]/config for sensor

[Your base channel]/switch/[Sensor Name]_stream/config for stream switch

[Your base channel]/sensor/[Sensor Name]_record/config for record switch

import json
import socket # Just so we can properly handle hostname exceptions
import obspython as obs
import paho.mqtt.client as mqtt
import ssl
import pathlib
import time
import enum
import uuid
# Meta
__version__ = '1.0.2'
__version_info__ = (1, 0, 1)
__license__ = "AGPLv3"
__license_info__ = {
"AGPLv3": {
"product": "update_mqtt_status_homeassistant",
"users": 0, # 0 being unlimited
"customer": "Unsupported",
"version": __version__,
"license_format": "1.0",
}
}
__author__ = 'HeedfulCrayon'
__doc__ = """\
Publishes real-time OBS status info to the given MQTT server/port/channel \
at the configured interval. Also opens up controllable aspects of OBS to \
be controlled through MQTT.
"""
# Default values for the configurable options:
INTERVAL = 5 # Update interval (in seconds)
MQTT_HOST = "localhost" # Hostname of your MQTT server
MQTT_USER = ""
MQTT_PW = ""
MQTT_PORT = 1883 # Default MQTT port is 1883
MQTT_BASE_CHANNEL = ""
MQTT_SENSOR_NAME = "obs"
PROFILES = []
STREAM_SWITCH = None
RECORD_SWITCH = None
SENSOR = None
CONTROL = False
DEBUG = False
LOCK = False
MAC = ':'.join(['{:02x}'.format((uuid.getnode() >> ele) & 0xff)
for ele in range(0,8*6,8)][::-1])
class SwitchType(str, enum.Enum):
profile = "profile"
record = "record"
stream = "stream"
class SwitchPayload(str, enum.Enum):
OFF = "OFF"
ON = "ON"
class Switch:
"""
Represents a controllable aspect of OBS (Profile, Record, Stream, etc.)
"""
def __init__(self):
self.publish_config()
self.subscribe()
self.publish_command(SwitchPayload.OFF)
def publish_config(self):
CLIENT.publish(self.config_topic, json.dumps(self.config))
if DEBUG: print(f"Published config {self.config['name']}")
def subscribe(self):
CLIENT.subscribe(self.command_topic)
if DEBUG: print(f"Subscribed to {self.config['name']}")
def publish_state(self, payload):
CLIENT.publish(self.state_topic, payload)
if DEBUG: print(f"{self.config['name']} state changed to {payload}")
def publish_command(self, payload):
CLIENT.publish(self.command_topic, payload)
if DEBUG: print(f"{self.config['name']} command published. Payload: {payload}")
class PersistentSwitch(Switch):
"""
Switch that is persisted (retained) in MQTT
"""
def __init__(self):
super().__init__()
self.publish_availability(SwitchPayload.ON)
def publish_config(self):
CLIENT.publish(self.config_topic, json.dumps(self.config), retain=True)
if DEBUG: print(f"Published config {self.config['name']}")
def publish_availability(self, payload):
CLIENT.publish(self.available_topic, payload)
if DEBUG: print(f"{self.config['name']} availability set to {payload}")
class ProfileSwitch(Switch):
def __init__(self, profile_name, mqtt_base_channel, mqtt_sensor_name):
self.profile_name = profile_name
self.mqtt_base_channel = mqtt_base_channel
self.mqtt_sensor_name = mqtt_sensor_name
self.switch_type = SwitchType.profile
self.state_topic = f"{self.mqtt_base_channel}/switch/{self.profile_name}/state"
self.command_topic = f"{self.mqtt_base_channel}/switch/{self.profile_name}/profile/set"
self.config_topic = f"{self.mqtt_base_channel}/switch/{self.profile_name}/config"
self.config = {
"name": f"{self.profile_name} Profile",
"unique_id": f"{self.mqtt_sensor_name}_{self.profile_name}_profile",
"device": {
"name": f"{self.mqtt_sensor_name}",
"identifiers": f"[['mac',{MAC}]]",
"manufacturer": f"OBS Script v.{__version__}",
"sw_version": __version__
},
"state_topic": self.state_topic,
"command_topic": self.command_topic,
"icon": f"mdi:alpha-{self.profile_name[0].lower()}-box",
"payload_on": SwitchPayload.ON,
"payload_off": SwitchPayload.OFF
}
super().__init__()
def publish_remove_config(self):
CLIENT.publish(self.config_topic, "")
if DEBUG: print(f"Removed config {self.config['name']}")
class StreamSwitch(PersistentSwitch):
def __init__(self, mqtt_base_channel, mqtt_sensor_name):
self.mqtt_base_channel = mqtt_base_channel
self.mqtt_sensor_name = mqtt_sensor_name
self.switch_type = SwitchType.stream
self.state_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}/stream/state"
self.command_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}/stream/set"
self.config_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}_stream/config"
self.available_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}/stream/available"
self.config = {
"name": f"{self.mqtt_sensor_name} Stream",
"unique_id": f"{self.mqtt_sensor_name}_stream",
"device": {
"name": f"{self.mqtt_sensor_name}",
"identifiers": f"[['mac',{MAC}]]",
"manufacturer": f"OBS Script v.{__version__}",
"sw_version": __version__
},
"state_topic": self.state_topic,
"command_topic": self.command_topic,
"payload_on": SwitchPayload.ON,
"payload_off": SwitchPayload.OFF,
"availability": {
"payload_available": SwitchPayload.ON,
"payload_not_available": SwitchPayload.OFF,
"topic": self.available_topic
},
"icon": "mdi:broadcast"
}
super().__init__()
class RecordSwitch(PersistentSwitch):
def __init__(self, mqtt_base_channel, mqtt_sensor_name):
self.mqtt_base_channel = mqtt_base_channel
self.mqtt_sensor_name = mqtt_sensor_name
self.switch_type = SwitchType.record
self.state_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}/record/state"
self.command_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}/record/set"
self.config_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}_record/config"
self.available_topic = f"{self.mqtt_base_channel}/switch/{self.mqtt_sensor_name}/record/available"
self.config = {
"name": f"{self.mqtt_sensor_name} Record",
"unique_id": f"{self.mqtt_sensor_name}_record",
"device": {
"name": f"{self.mqtt_sensor_name}",
"identifiers": f"[['mac',{MAC}]]",
"manufacturer": f"OBS Script v.{__version__}",
"sw_version": __version__
},
"state_topic": self.state_topic,
"command_topic": self.command_topic,
"payload_on": SwitchPayload.ON,
"payload_off": SwitchPayload.OFF,
"availability": {
"payload_available": SwitchPayload.ON,
"payload_not_available": SwitchPayload.OFF,
"topic": self.available_topic
},
"icon": "mdi:record"
}
super().__init__()
class SensorState(str, enum.Enum):
Off = "Off"
Stopped = "Stopped"
Recording = "Recording"
Streaming = "Streaming"
Recording_and_Streaming = "Recording and Streaming"
class Sensor:
def __init__(self, mqtt_base_channel, mqtt_sensor_name):
self.mqtt_base_channel = mqtt_base_channel
self.mqtt_sensor_name = mqtt_sensor_name
self.state_topic = f"{self.mqtt_base_channel}/sensor/{self.mqtt_sensor_name}/state"
self.config_topic = f"{self.mqtt_base_channel}/sensor/{self.mqtt_sensor_name}/config"
self.attributes_topic = f"{self.mqtt_base_channel}/sensor/{self.mqtt_sensor_name}/attributes"
self.config = {
"name": self.mqtt_sensor_name,
"unique_id": self.mqtt_sensor_name,
"device": {
"name": f"{self.mqtt_sensor_name}",
"identifiers": f"[['mac',{MAC}]]",
"manufacturer": f"OBS Script v.{__version__}",
"sw_version": __version__
},
"state_topic": self.state_topic,
"json_attributes_topic": self.attributes_topic
}
self.state = self.get_state
self.previous_state = SensorState.Off
self.recording = obs.obs_frontend_recording_active
self.streaming = obs.obs_frontend_streaming_active
self.paused = obs.obs_frontend_recording_paused
self.replay_buffer =obs.obs_frontend_replay_buffer_active
self.fps = obs.obs_get_active_fps
self.frame_time_ns = obs.obs_get_average_frame_time_ns
self.frames = obs.obs_get_total_frames
self.lagged_frames = obs.obs_get_lagged_frames
self.active = False
self.publish_config()
self.publish_state()
self.publish_attributes()
def publish_config(self):
CLIENT.publish(self.config_topic, json.dumps(self.config))
if DEBUG: print(f"Published config {self.config['name']}")
def publish_attributes(self):
stats = {
"recording": self.recording(),
"streaming": self.streaming(),
"paused": self.paused(),
"fps": self.fps(),
"frame_time_ns": self.frame_time_ns(),
"frames": self.frames(),
"lagged_frames": self.lagged_frames()
}
CLIENT.publish(self.attributes_topic, json.dumps(stats))
self.publish_state()
if DEBUG:
print(f"{self.config['name']} attributes updated")
print(json.dumps(stats))
def get_state(self):
recording = self.recording()
streaming = self.streaming()
if recording and streaming:
self.active = True
self.previous_state = SensorState.Recording_and_Streaming
return SensorState.Recording_and_Streaming
elif streaming:
self.active = True
self.previous_state = SensorState.Streaming
return SensorState.Streaming
elif recording:
self.active = True
self.previous_state = SensorState.Recording
return SensorState.Recording
else:
self.active = False
self.previous_state = SensorState.Stopped
return SensorState.Stopped
def publish_state(self):
state = self.state()
CLIENT.publish(self.state_topic, state)
if DEBUG: print(f"{self.config['name']} state changed to {state}")
def publish_off_state(self):
self.previous_state = SensorState.Off
CLIENT.publish(self.state_topic, SensorState.Off)
if DEBUG: print(f"{self.config['name']} state changed to {SensorState.Off}")
# MQTT Event Functions
def on_mqtt_connect(client, userdata, flags, rc):
"""
Called when the MQTT client is connected from the server. Just prints a
message indicating we connected successfully.
"""
print("MQTT connection successful")
set_homeassistant_config()
def on_mqtt_disconnect(client, userdata, rc):
"""
Called when the MQTT client gets disconnected. Just logs a message about it
(we'll auto-reconnect inside of update_status()).
"""
print("MQTT disconnected. Reason: {}".format(str(rc)))
def on_mqtt_message(client, userdata, message):
"""
Handles MQTT messages that have been subscribed to
"""
payload = str(message.payload.decode("utf-8"))
if DEBUG: print(f"{message.topic}: {payload}")
entity = message_to_switch_entity(message)
if entity != None:
execute_action(entity, payload)
# OBS Script Function Exports
def script_description():
return __doc__ # We wrote a nice docstring... Might as well use it!
def script_load(settings):
"""
Just prints a message indicating that the script was loaded successfully.
"""
global STATE
print("MQTT script loaded.")
STATE = "Initializing"
def script_unload():
"""
Publishes a final status message indicating OBS is off
(so your MQTT sensor doesn't get stuck thinking you're
recording/streaming forever) and calls `CLIENT.disconnect()`.
"""
global STATE
print("Script unloading")
STATE = "Off"
if CLIENT.is_connected():
SENSOR.publish_off_state()
set_persistent_switch_availability()
remove_profiles_from_homeassistant()
time.sleep(0.5)
CLIENT.disconnect()
CLIENT.loop_stop()
def script_defaults(settings):
"""
Sets up our default settings in the OBS Scripts interface.
"""
obs.obs_data_set_default_string(settings, "mqtt_host", MQTT_HOST)
obs.obs_data_set_default_string(settings, "mqtt_user", MQTT_USER)
obs.obs_data_set_default_string(settings, "mqtt_pw", MQTT_PW)
obs.obs_data_set_default_string(settings, "mqtt_base_channel", MQTT_BASE_CHANNEL)
obs.obs_data_set_default_string(settings, "mqtt_sensor_name", MQTT_SENSOR_NAME)
obs.obs_data_set_default_int(settings, "mqtt_port", MQTT_PORT)
obs.obs_data_set_default_int(settings, "interval", INTERVAL)
obs.obs_data_set_default_bool(settings, "controllable", CONTROL)
def script_properties():
"""
Makes this script's settings configurable via OBS's Scripts GUI.
"""
props = obs.obs_properties_create()
obs.obs_properties_add_text(props, "mqtt_host", "MQTT server hostname", obs.OBS_TEXT_DEFAULT)
obs.obs_properties_add_text(props, "mqtt_user", "MQTT username", obs.OBS_TEXT_DEFAULT)
obs.obs_properties_add_text(props, "mqtt_pw", "MQTT password", obs.OBS_TEXT_PASSWORD)
obs.obs_properties_add_text(props, "mqtt_base_channel", "MQTT Base channel",obs.OBS_TEXT_DEFAULT)
obs.obs_properties_add_text(props, "mqtt_sensor_name", "MQTT Sensor Name",obs.OBS_TEXT_DEFAULT)
obs.obs_properties_add_int(props, "mqtt_port", "MQTT TCP/IP port", MQTT_PORT, 65535, 1)
obs.obs_properties_add_int(props, "interval", "Update Interval (seconds)", 1, 3600, 1)
obs.obs_properties_add_bool(props, "controllable", "Control Streaming/Recording via MQTT")
obs.obs_properties_add_bool(props, "debug", "Debug")
return props
def script_update(settings):
"""
Applies any changes made to the MQTT settings in the OBS Scripts GUI then
reconnects the MQTT client.
"""
# Apply the new settings
global MQTT_HOST
global MQTT_USER
global MQTT_PW
global MQTT_PORT
global MQTT_BASE_CHANNEL
global MQTT_SENSOR_NAME
global INTERVAL
global CONTROL
global DEBUG
mqtt_host = obs.obs_data_get_string(settings, "mqtt_host")
if mqtt_host != MQTT_HOST:
MQTT_HOST = mqtt_host
mqtt_user = obs.obs_data_get_string(settings, "mqtt_user")
if mqtt_user != MQTT_USER:
MQTT_USER = mqtt_user
mqtt_pw = obs.obs_data_get_string(settings, "mqtt_pw")
if mqtt_pw != MQTT_PW:
MQTT_PW = mqtt_pw
mqtt_base_channel = obs.obs_data_get_string(settings, "mqtt_base_channel")
if mqtt_base_channel != MQTT_BASE_CHANNEL:
MQTT_BASE_CHANNEL = mqtt_base_channel
mqtt_sensor_name = obs.obs_data_get_string(settings, "mqtt_sensor_name")
if mqtt_sensor_name != MQTT_SENSOR_NAME:
MQTT_SENSOR_NAME = mqtt_sensor_name
mqtt_port = obs.obs_data_get_int(settings, "mqtt_port")
if mqtt_port != MQTT_PORT:
MQTT_PORT = mqtt_port
INTERVAL = obs.obs_data_get_int(settings, "interval")
CONTROL = obs.obs_data_get_bool(settings, "controllable")
DEBUG = obs.obs_data_get_bool(settings, "debug")
# Disconnect (if connected) and reconnect the MQTT client
CLIENT.disconnect()
try:
if MQTT_PW != "" and MQTT_USER != "":
CLIENT.username_pw_set(MQTT_USER, password=MQTT_PW)
CLIENT.connect_async(MQTT_HOST, MQTT_PORT, 60)
except (socket.gaierror, ConnectionRefusedError) as e:
print("NOTE: Got a socket issue: %s" % e)
pass # Ignore it for now
obs.obs_frontend_remove_event_callback(frontend_changed)
obs.obs_frontend_add_event_callback(frontend_changed)
# Remove and replace the timer that publishes our status information
obs.timer_remove(update_status)
obs.timer_add(update_status, INTERVAL * 1000)
CLIENT.loop_start()
def frontend_changed(event):
"""
Callback for frontend events
"""
switcher = {
obs.OBS_FRONTEND_EVENT_PROFILE_CHANGED: profile_changed,
obs.OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED: profile_list_changed,
obs.OBS_FRONTEND_EVENT_RECORDING_STARTED: recording_started,
obs.OBS_FRONTEND_EVENT_RECORDING_STOPPED: recording_stopped,
obs.OBS_FRONTEND_EVENT_STREAMING_STARTED: streaming_started,
obs.OBS_FRONTEND_EVENT_STREAMING_STOPPED: streaming_stopped
}
function = switcher.get(event, None)
if function != None:
function()
else:
print(f"Unknown event fired: {event}")
def profile_changed():
"""
Callback for OBS_FRONTEND_EVENT_PROFILE_CHANGED
"""
global PROFILE
while LOCK:
time.sleep(0.5)
PROFILE.publish_state(SwitchPayload.OFF)
new_profile = obs.obs_frontend_get_current_profile()
for profile in PROFILES:
if profile.profile_name == new_profile:
profile.publish_state(SwitchPayload.ON)
PROFILE = profile
print("Profile Changed")
def profile_list_changed():
"""
Callback for OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED
"""
global LOCK
LOCK = True
for profile in PROFILES:
profile.publish_remove_config()
time.sleep(0.1)
setup_profiles_in_homeassistant()
LOCK = False
print("Profile List Changed")
def recording_started():
"""
Publishes state of sensor and record switch
"""
SENSOR.publish_state()
SENSOR.publish_attributes()
if CONTROL:
RECORD_SWITCH.publish_state(SwitchPayload.ON)
def recording_stopped():
"""
Publishes state of sensor and record switch
"""
SENSOR.publish_state()
if CONTROL:
RECORD_SWITCH.publish_state(SwitchPayload.OFF)
def streaming_started():
"""
Publishes state of sensor and stream switch
"""
SENSOR.publish_state()
SENSOR.publish_attributes()
if CONTROL:
STREAM_SWITCH.publish_state(SwitchPayload.ON)
def streaming_stopped():
"""
Publishes state of sensor and stream switch
"""
SENSOR.publish_state()
if CONTROL:
STREAM_SWITCH.publish_state(SwitchPayload.OFF)
# Event Helper Functions
def set_homeassistant_config():
"""
Sends initial configuration state and attributes topic
for autodiscovery in Home Assistant
"""
global SENSOR
SENSOR = Sensor(MQTT_BASE_CHANNEL, MQTT_SENSOR_NAME)
if CONTROL:
setup_homeassistant_control()
def setup_homeassistant_control():
"""
Sets up profile, recording and streaming controls
"""
global STREAM_SWITCH
global RECORD_SWITCH
setup_profiles_in_homeassistant()
# Set up switches for autodiscovery
STREAM_SWITCH = StreamSwitch(MQTT_BASE_CHANNEL, MQTT_SENSOR_NAME)
RECORD_SWITCH = RecordSwitch(MQTT_BASE_CHANNEL, MQTT_SENSOR_NAME)
def setup_profiles_in_homeassistant():
"""
Publishes config, and subscribes to the command topic for each profile.
Also sets the current profile's state
"""
global PROFILE
global PROFILES
current_profile = obs.obs_frontend_get_current_profile()
profiles = obs.obs_frontend_get_profiles()
PROFILES = []
for profile in profiles:
profile_switch = ProfileSwitch(
profile_name=profile,
mqtt_base_channel=MQTT_BASE_CHANNEL,
mqtt_sensor_name=MQTT_SENSOR_NAME
)
PROFILES.append(profile_switch)
if DEBUG: print(f"Profile {profile_switch.profile_name} added to PROFILES")
if profile_switch.profile_name == current_profile:
PROFILE = profile_switch
profile_switch.publish_state(SwitchPayload.ON)
def set_persistent_switch_availability():
"""
Reports the availability of the persistent switches
"""
RECORD_SWITCH.publish_availability(SwitchPayload.OFF)
STREAM_SWITCH.publish_availability(SwitchPayload.OFF)
def remove_profiles_from_homeassistant():
"""
Profiles are removed when obs is not open
"""
global PROFILES
for profile in PROFILES:
profile.publish_remove_config()
PROFILES = []
def execute_action(switch, payload):
"""
Executes frontend actions (Profile change, recording, streaming)
"""
if switch.switch_type == SwitchType.profile:
if SENSOR.active: # Not sure if this NEEDS to be here as it won't actually change the profile while streaming/recording
return
prev_profile = obs.obs_frontend_get_current_profile()
if PROFILE.profile_name != switch.profile_name:
obs.obs_frontend_set_current_profile(switch.profile_name)
else:
print(f"Already on profile {switch.profile_name}")
elif switch.switch_type == SwitchType.stream:
if payload == SwitchPayload.ON:
obs.obs_frontend_streaming_start()
else:
obs.obs_frontend_streaming_stop()
elif switch.switch_type == SwitchType.record:
if payload == SwitchPayload.ON:
obs.obs_frontend_recording_start()
else:
obs.obs_frontend_recording_stop()
# Helper Functions
def update_status():
"""
Updates the STATE and the STATUS global with the stats of the current session.
This info if published (JSON-encoded) to the configured MQTT_HOST/MQTT_PORT/MQTT_BASE_CHANNEL.
Meant to be called at the configured INTERVAL.
"""
global SENSOR
if CONTROL:
STREAM_SWITCH.publish_availability(SwitchPayload.ON)
RECORD_SWITCH.publish_availability(SwitchPayload.ON)
previous_state = SENSOR.previous_state
if previous_state != SensorState.Stopped and SENSOR.state() == SensorState.Stopped:
print("Publishing Final Stopped Message")
SENSOR.publish_attributes()
if previous_state != SensorState.Off and SENSOR.state() == SensorState.Off:
print("Publishing Final Off Message")
SENSOR.publish_attributes()
if SENSOR.active:
SENSOR.publish_attributes()
def message_to_switch_entity(message):
"""
Converts MQTT Message to the corresponding switch entity
"""
topic = pathlib.PurePosixPath(message.topic)
message_type = SwitchType[topic.parent.stem] # Stream, Record or Profile
if message_type == SwitchType.stream:
return STREAM_SWITCH
if message_type == SwitchType.record:
return RECORD_SWITCH
if message_type == SwitchType.profile:
for profile in PROFILES:
if profile.command_topic == message.topic:
return profile
return None
# Using a global MQTT client variable to keep things simple:
CLIENT = mqtt.Client()
CLIENT.on_connect = on_mqtt_connect
CLIENT.on_disconnect = on_mqtt_disconnect
CLIENT.on_message = on_mqtt_message
@ASchneiderBR
Copy link

ASchneiderBR commented Apr 29, 2021

Hello sir! Thanks for the script.

I've tried it but it is asking for a module named Paho, mqtt client.

[update_mqtt_status_homeassistant.py] Traceback (most recent call last):
[update_mqtt_status_homeassistant.py]   File "C:/OBS Antonio Portable/my stuff/Scripts\update_mqtt_status_homeassistant.py", line 4, in <module>
[update_mqtt_status_homeassistant.py]     import paho.mqtt.client as mqtt
[update_mqtt_status_homeassistant.py] ModuleNotFoundError: No module named 'paho'

Do I need to do something eles?

I'm running a portable version of both OBS and Python installation.

Thanks.

@ASchneiderBR
Copy link

ASchneiderBR commented Apr 29, 2021

Sorry, just did a pip install paho-mqtt command on that portable installation and now it works :) Thanks!

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Apr 29, 2021

@tatunts Sorry, I forgot to add that to the write up. I will update it

@ASchneiderBR
Copy link

ASchneiderBR commented Apr 29, 2021

@tatunts Sorry, I forgot to add that to the write up. I will update it

No worries!

Since I have autodiscovery off, I had to add it manually. If you would like to share this, it might help others as well :)

- platform: "mqtt"
  name: "OBS"
  state_topic: "obs/status/state"
  icon: "mdi:video-wireless"
  force_update: true
  qos: 1
  json_attributes_topic: "obs/status/attributes"

Thanks!

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Apr 29, 2021

Thanks @tatunts! Updated

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Apr 29, 2021

Just FYI, I am attempting to modify this so that Home Assistant can start or stop streaming/recording as a toggle. I am very new to python, so it will take me a bit

@ASchneiderBR
Copy link

ASchneiderBR commented Apr 29, 2021

Amazing! That would be great! Another thing you could change in the future is to only send MQTT updates when there are new updates from OBS. For example, when OBS is OFF, not doing anything whatsoever, MQTT messages are still being sent according to the interval settings. Not a big deal at all, just something to keep in mind (it keeps changing the sensor state and, maybe, triggering automations if set?) :) Thanks a lot and good job mate!

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Apr 29, 2021

@tatunts it shouldn't be sending MQTT attribute update messages when streaming or recording is not going. lines 108-113 should take care of that I think. I am totally open for suggestions to make this better as I am pretty unfamiliar with python and the OBS API

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Apr 29, 2021

script_parameters

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Apr 29, 2021

@tatunts now you can control it :)

@ASchneiderBR
Copy link

ASchneiderBR commented Apr 29, 2021

Nice work @HeedfulCrayon !!

@ASchneiderBR
Copy link

ASchneiderBR commented Apr 29, 2021

After updating the file, I get this:

[update_mqtt_status_homeassistant.py] Traceback (most recent call last):
[update_mqtt_status_homeassistant.py]   File "C:/OBS Antonio Portable/my stuff/Scripts\update_mqtt_status_homeassistant.py", line 56, in <module>
[update_mqtt_status_homeassistant.py]     CLIENT.on_connect = on_mqtt_connect
[update_mqtt_status_homeassistant.py] NameError: name 'on_mqtt_connect' is not defined

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Apr 29, 2021

@tatunts fixed, was a simple issue. The event assignment was called before the function was defined

@ASchneiderBR
Copy link

ASchneiderBR commented Apr 29, 2021

Working great!! Thanks a lot!

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented May 3, 2021

Now you can switch between profiles!
Eventually I will get around to switching scenes

@ASchneiderBR
Copy link

ASchneiderBR commented May 3, 2021

Nice! It is getting better and better :)

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented May 3, 2021

Sorry, one more quick update. I had to fix an issue with profile renaming

@TristonFelix
Copy link

TristonFelix commented May 23, 2021

Maybe im just dumb and overlooking something but when i add it to obs i get "no properties available"

@ASchneiderBR
Copy link

ASchneiderBR commented May 23, 2021

Maybe im just dumb and overlooking something but when i add it to obs i get "no properties available"

Hey there! This means you either need Python installed on your system or download the PAHO modules using CLI.

@TristonFelix
Copy link

TristonFelix commented May 23, 2021

Maybe im just dumb and overlooking something but when i add it to obs i get "no properties available"

Hey there! This means you either need Python installed on your system or download the PAHO modules using CLI.

i do have python install and obs is pointed to it thats why im stumped :/ my other python scripts work just fine as well
i do get this error tho when adding it [update_mqtt_status_homeassistant.py] Traceback (most recent call last): [update_mqtt_status_homeassistant.py] File "V:/Users/triston.InterXet/Downloads/Compressed/9ff74d2aa1bc629ed17e0780f9a91a3d-5e58bcdd5ce0329eaa6172628206a680d5a03630/9ff74d2aa1bc629ed17e0780f9a91a3d-5e58bcdd5ce0329eaa6172628206a680d5a03630\update_mqtt_status_homeassistant.py", line 4, in <module> [update_mqtt_status_homeassistant.py] import paho.mqtt.client as mqtt [update_mqtt_status_homeassistant.py] ModuleNotFoundError: No module named 'paho'

@ASchneiderBR
Copy link

ASchneiderBR commented May 23, 2021

Maybe im just dumb and overlooking something but when i add it to obs i get "no properties available"

Hey there! This means you either need Python installed on your system or download the PAHO modules using CLI.

i do have python install and obs is pointed to it thats why im stumped :/ my other python scripts work just fine as well
i do get this error tho when adding it [update_mqtt_status_homeassistant.py] Traceback (most recent call last): [update_mqtt_status_homeassistant.py] File "V:/Users/triston.InterXet/Downloads/Compressed/9ff74d2aa1bc629ed17e0780f9a91a3d-5e58bcdd5ce0329eaa6172628206a680d5a03630/9ff74d2aa1bc629ed17e0780f9a91a3d-5e58bcdd5ce0329eaa6172628206a680d5a03630\update_mqtt_status_homeassistant.py", line 4, in <module> [update_mqtt_status_homeassistant.py] import paho.mqtt.client as mqtt [update_mqtt_status_homeassistant.py] ModuleNotFoundError: No module named 'paho'

So that's your problem, like I've said before. You need to install the MQTT module, please refer to the setup instructions in the beginning. You need to open Python command line (from the Python installation that you have linked to your OBS) and run pip install mqtt-wrapper to install the paho module, as stated in your log.

@Mattie
Copy link

Mattie commented Jun 20, 2021

Thanks so much for the awesome script. Very useful!
I did a quicky-fork to add support for the virtual camera button. Feel free to use it here if you wanna. For me, this is useful for turning on "ON AIR" lights and similar when I'm using my customized webcam for video conferences (zoom, teams, slack, etc). Thanks again for taking the time to share this.

@Lorsel
Copy link

Lorsel commented Dec 11, 2021

i have installed the mqtt-wrapper, but i still this error

[update_mqtt_status_homeassistant_controllable.py] Traceback (most recent call last):
[update_mqtt_status_homeassistant_controllable.py]   File "D:/Utenti/xelae/Documents/obs/homeassistant/sensor/obs\update_mqtt_status_homeassistant_controllable.py", line 4, in <module>
[update_mqtt_status_homeassistant_controllable.py]     import paho.mqtt.client as mqtt
[update_mqtt_status_homeassistant_controllable.py] ModuleNotFoundError: No module named 'paho'
C:\Users\xelae>C:\Users\xelae\AppData\Local\Programs\Python\Python310\python.exe -m pip list
Package      Version
------------ -------
mqtt-wrapper 0.5
paho-mqtt    1.6.1
pip          21.3.1
setuptools   58.1.0
wheel        0.37.0

@jenny787
Copy link

jenny787 commented Jan 29, 2022

installing this script makes my obs freeze for several seconds every time it refreshes the status.

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Jan 30, 2022

@jenny787 I haven't even looked at this script in a year. I don't stream anymore, so I don't even have anything to test it with. Also, I am quite new to python, so I am sure there are things that could be done more efficiently. Feel free to give it a shot.
It also could be that a new version of OBS doesn't play nice with python scripting... not sure

@jamesking210
Copy link

jamesking210 commented Jun 12, 2022

has anyone forked this to output what scene your on and possibly change scenes? or virtual camera on/off?

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Jun 19, 2022

has anyone forked this to output what scene your on and possibly change scenes? or virtual camera on/off?

Yes, I believe @Mattie's fork has a sensor for the virtual camera, as for changing scenes, I believe both Mattie's and my version will handle that. It has been a while since I worked on it though

@jt001sk
Copy link

jt001sk commented Sep 17, 2022

i have installed the mqtt-wrapper, but i still this error

[update_mqtt_status_homeassistant_controllable.py] Traceback (most recent call last):
[update_mqtt_status_homeassistant_controllable.py]   File "D:/Utenti/xelae/Documents/obs/homeassistant/sensor/obs\update_mqtt_status_homeassistant_controllable.py", line 4, in <module>
[update_mqtt_status_homeassistant_controllable.py]     import paho.mqtt.client as mqtt
[update_mqtt_status_homeassistant_controllable.py] ModuleNotFoundError: No module named 'paho'
C:\Users\xelae>C:\Users\xelae\AppData\Local\Programs\Python\Python310\python.exe -m pip list
Package      Version
------------ -------
mqtt-wrapper 0.5
paho-mqtt    1.6.1
pip          21.3.1
setuptools   58.1.0
wheel        0.37.0

Hi,
I had similar issue. It was caused by multiple versions of python installed. Please verify, which version is OBS using: Tools > Scripts - Python Settings [panel tab]
In my case, I installed mqtt-wrapper to wrong version of python (other than OBS was using) at first.

@jt001sk
Copy link

jt001sk commented Sep 17, 2022

@HeedfulCrayon thank you for your work. I believe that there are few bugs related to non-control mode. Script tries to publish_state of switches (streaming+recording) even if they do not exist (because of non-control mode). I added few (five) conditions to fix it (I do not know better way, so I will paste screenshots with differences - left is modified/fixed, right is your original):
image
image

@HeedfulCrayon
Copy link
Author

HeedfulCrayon commented Sep 19, 2022

@jt001sk I updated the gist with those if CONTROL: conditions, thanks for pointing that out. One of these days I plan to implement what @Mattie 's fork does with the virtual cam as well. Like I said though, I have not streamed in a while, so I actually don't use this tool myself anymore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment