or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdevent-client.mdevent-subscriptions.mdindex.mdrequest-client.md

event-client.mddocs/

0

# Event Client

1

2

The EventClient provides an asynchronous interface for receiving and handling real-time events from OBS Studio. It uses a callback-based system where you register handler functions that are triggered when specific events occur.

3

4

## Core Event System

5

6

### Event Client Initialization

7

8

```python { .api }

9

class EventClient:

10

def __init__(self, host='localhost', port=4455, password='', subs=Subs.LOW_VOLUME, timeout=None):

11

"""

12

Initialize event client.

13

14

Parameters:

15

- host (str): OBS WebSocket host address

16

- port (int): OBS WebSocket port number

17

- password (str): OBS WebSocket password

18

- subs (int): Event subscription flags (Subs enum values)

19

- timeout (float, optional): Connection timeout in seconds

20

"""

21

22

def __enter__(self):

23

"""Context manager entry."""

24

25

def __exit__(self, exc_type, exc_value, exc_traceback):

26

"""Context manager exit with automatic disconnect."""

27

28

def disconnect(self):

29

"""Stop listening for events and close connection."""

30

31

unsubscribe = disconnect # Alias for disconnect

32

```

33

34

### Callback Management

35

36

The EventClient provides a `callback` property that manages event handler registration:

37

38

```python { .api }

39

class Callback:

40

def register(self, fns):

41

"""

42

Register callback function(s) for events.

43

44

Parameters:

45

- fns (callable or list): Single function or list of functions to register

46

"""

47

48

def deregister(self, fns):

49

"""

50

Deregister callback function(s).

51

52

Parameters:

53

- fns (callable or list): Single function or list of functions to deregister

54

"""

55

56

def get(self):

57

"""

58

Get list of registered event names.

59

60

Returns:

61

List of event names (CamelCase) that have registered handlers

62

"""

63

64

def clear(self):

65

"""Clear all registered callback functions."""

66

```

67

68

## Event Handler Conventions

69

70

Event handler functions must follow specific naming conventions:

71

72

- Use **snake_case** naming with **"on_"** prefix

73

- Function name must match the OBS event name converted to snake_case

74

- Handler receives a single `data` parameter containing event information

75

76

### Event Name Mapping

77

78

OBS Event Name → Handler Function Name:

79

- `SceneCreated``on_scene_created`

80

- `InputMuteStateChanged``on_input_mute_state_changed`

81

- `CurrentProgramSceneChanged``on_current_program_scene_changed`

82

- `StreamStateChanged``on_stream_state_changed`

83

84

### Event Data Access

85

86

Event data is provided as dataclass objects with snake_case attributes:

87

88

```python

89

def on_scene_created(data):

90

# Access event attributes

91

print(f"Scene created: {data.scene_name}")

92

print(f"Is group: {data.is_group}")

93

94

# Inspect all available attributes

95

print(f"Available attributes: {data.attrs()}")

96

```

97

98

## Common Event Categories

99

100

### Scene Events

101

102

```python

103

def on_scene_created(data):

104

"""

105

Triggered when a scene is created.

106

107

Event data attributes:

108

- scene_name (str): Name of created scene

109

- is_group (bool): Whether scene is a group

110

"""

111

112

def on_scene_removed(data):

113

"""

114

Triggered when a scene is removed.

115

116

Event data attributes:

117

- scene_name (str): Name of removed scene

118

- is_group (bool): Whether scene was a group

119

"""

120

121

def on_scene_name_changed(data):

122

"""

123

Triggered when a scene is renamed.

124

125

Event data attributes:

126

- old_scene_name (str): Previous scene name

127

- scene_name (str): New scene name

128

"""

129

130

def on_current_program_scene_changed(data):

131

"""

132

Triggered when program scene changes.

133

134

Event data attributes:

135

- scene_name (str): New program scene name

136

"""

137

138

def on_current_preview_scene_changed(data):

139

"""

140

Triggered when preview scene changes.

141

142

Event data attributes:

143

- scene_name (str): New preview scene name

144

"""

145

146

def on_scene_list_changed(data):

147

"""

148

Triggered when scene list changes.

149

150

Event data attributes:

151

- scenes (list): Updated scene list

152

"""

153

```

154

155

### Input Events

156

157

```python

158

def on_input_created(data):

159

"""

160

Triggered when an input is created.

161

162

Event data attributes:

163

- input_name (str): Name of created input

164

- input_kind (str): Type/kind of input

165

- unversioned_input_kind (str): Unversioned input kind

166

- input_settings (dict): Input settings

167

- default_input_settings (dict): Default settings

168

"""

169

170

def on_input_removed(data):

171

"""

172

Triggered when an input is removed.

173

174

Event data attributes:

175

- input_name (str): Name of removed input

176

"""

177

178

def on_input_name_changed(data):

179

"""

180

Triggered when input is renamed.

181

182

Event data attributes:

183

- old_input_name (str): Previous input name

184

- input_name (str): New input name

185

"""

186

187

def on_input_mute_state_changed(data):

188

"""

189

Triggered when input mute state changes.

190

191

Event data attributes:

192

- input_name (str): Input name

193

- input_muted (bool): New mute state

194

"""

195

196

def on_input_volume_changed(data):

197

"""

198

Triggered when input volume changes.

199

200

Event data attributes:

201

- input_name (str): Input name

202

- input_volume_mul (float): Volume multiplier

203

- input_volume_db (float): Volume in dB

204

"""

205

206

def on_input_settings_changed(data):

207

"""

208

Triggered when input settings change.

209

210

Event data attributes:

211

- input_name (str): Input name

212

- input_settings (dict): New input settings

213

"""

214

```

215

216

### Stream and Record Events

217

218

```python

219

def on_stream_state_changed(data):

220

"""

221

Triggered when streaming state changes.

222

223

Event data attributes:

224

- output_active (bool): Stream active state

225

- output_state (str): Stream state name

226

"""

227

228

def on_record_state_changed(data):

229

"""

230

Triggered when recording state changes.

231

232

Event data attributes:

233

- output_active (bool): Recording active state

234

- output_state (str): Recording state name

235

- output_path (str, optional): Recording file path

236

"""

237

238

def on_record_file_changed(data):

239

"""

240

Triggered when recording file changes.

241

242

Event data attributes:

243

- new_output_path (str): New recording file path

244

"""

245

246

def on_replay_buffer_state_changed(data):

247

"""

248

Triggered when replay buffer state changes.

249

250

Event data attributes:

251

- output_active (bool): Replay buffer active state

252

- output_state (str): Replay buffer state name

253

"""

254

255

def on_replay_buffer_saved(data):

256

"""

257

Triggered when replay buffer is saved.

258

259

Event data attributes:

260

- saved_replay_path (str): Path to saved replay file

261

"""

262

```

263

264

### Scene Item Events

265

266

```python

267

def on_scene_item_created(data):

268

"""

269

Triggered when scene item is created.

270

271

Event data attributes:

272

- scene_name (str): Scene name

273

- source_name (str): Source name

274

- scene_item_id (int): Scene item ID

275

- scene_item_index (int): Scene item index

276

"""

277

278

def on_scene_item_removed(data):

279

"""

280

Triggered when scene item is removed.

281

282

Event data attributes:

283

- scene_name (str): Scene name

284

- source_name (str): Source name

285

- scene_item_id (int): Scene item ID

286

"""

287

288

def on_scene_item_enable_state_changed(data):

289

"""

290

Triggered when scene item enable state changes.

291

292

Event data attributes:

293

- scene_name (str): Scene name

294

- scene_item_id (int): Scene item ID

295

- scene_item_enabled (bool): New enable state

296

"""

297

298

def on_scene_item_lock_state_changed(data):

299

"""

300

Triggered when scene item lock state changes.

301

302

Event data attributes:

303

- scene_name (str): Scene name

304

- scene_item_id (int): Scene item ID

305

- scene_item_locked (bool): New lock state

306

"""

307

308

def on_scene_item_transform_changed(data):

309

"""

310

Triggered when scene item transform changes (HIGH_VOLUME event).

311

312

Event data attributes:

313

- scene_name (str): Scene name

314

- scene_item_id (int): Scene item ID

315

- scene_item_transform (dict): New transform data

316

"""

317

```

318

319

### Filter Events

320

321

```python

322

def on_source_filter_created(data):

323

"""

324

Triggered when source filter is created.

325

326

Event data attributes:

327

- source_name (str): Source name

328

- filter_name (str): Filter name

329

- filter_kind (str): Filter type

330

- filter_index (int): Filter index

331

- filter_settings (dict): Filter settings

332

- default_filter_settings (dict): Default settings

333

"""

334

335

def on_source_filter_removed(data):

336

"""

337

Triggered when source filter is removed.

338

339

Event data attributes:

340

- source_name (str): Source name

341

- filter_name (str): Filter name

342

"""

343

344

def on_source_filter_enable_state_changed(data):

345

"""

346

Triggered when filter enable state changes.

347

348

Event data attributes:

349

- source_name (str): Source name

350

- filter_name (str): Filter name

351

- filter_enabled (bool): New enable state

352

"""

353

```

354

355

## Usage Examples

356

357

### Basic Event Listening

358

359

```python

360

import obsws_python as obs

361

362

# Create event client with low-volume events

363

client = obs.EventClient(subs=obs.Subs.LOW_VOLUME)

364

365

# Define event handlers

366

def on_scene_created(data):

367

print(f"New scene created: {data.scene_name}")

368

369

def on_input_mute_state_changed(data):

370

print(f"Input '{data.input_name}' mute changed to: {data.input_muted}")

371

372

def on_stream_state_changed(data):

373

if data.output_active:

374

print("Stream started!")

375

else:

376

print("Stream stopped!")

377

378

# Register callbacks

379

client.callback.register([

380

on_scene_created,

381

on_input_mute_state_changed,

382

on_stream_state_changed

383

])

384

385

# Keep client running

386

try:

387

input("Press Enter to stop listening...\n")

388

finally:

389

client.disconnect()

390

```

391

392

### Event Logging System

393

394

```python

395

import obsws_python as obs

396

import logging

397

from datetime import datetime

398

399

# Setup logging

400

logging.basicConfig(level=logging.INFO)

401

logger = logging.getLogger(__name__)

402

403

def log_event(event_name):

404

"""Decorator to log event occurrences."""

405

def decorator(func):

406

def wrapper(data):

407

timestamp = datetime.now().strftime("%H:%M:%S")

408

logger.info(f"[{timestamp}] {event_name}: {data.attrs()}")

409

return func(data)

410

return wrapper

411

return decorator

412

413

# Event handlers with logging

414

@log_event("Scene Changed")

415

def on_current_program_scene_changed(data):

416

print(f"Switched to scene: {data.scene_name}")

417

418

@log_event("Input Created")

419

def on_input_created(data):

420

print(f"New input: {data.input_name} ({data.input_kind})")

421

422

@log_event("Recording State")

423

def on_record_state_changed(data):

424

state = "started" if data.output_active else "stopped"

425

print(f"Recording {state}")

426

if hasattr(data, 'output_path') and data.output_path:

427

print(f"Recording to: {data.output_path}")

428

429

# Initialize client and register handlers

430

with obs.EventClient(subs=obs.Subs.LOW_VOLUME) as client:

431

client.callback.register([

432

on_current_program_scene_changed,

433

on_input_created,

434

on_record_state_changed

435

])

436

437

print("Event logging started. Registered events:")

438

for event in client.callback.get():

439

print(f" - {event}")

440

441

input("Press Enter to stop...\n")

442

```

443

444

### High-Volume Event Monitoring

445

446

```python

447

import obsws_python as obs

448

import time

449

450

# Monitor high-frequency events

451

client = obs.EventClient(subs=obs.Subs.HIGH_VOLUME)

452

453

volume_updates = 0

454

transform_updates = 0

455

456

def on_input_volume_meters(data):

457

"""Handle volume meter updates (very frequent)."""

458

global volume_updates

459

volume_updates += 1

460

461

# Log every 100 updates to avoid spam

462

if volume_updates % 100 == 0:

463

print(f"Volume meter updates: {volume_updates}")

464

465

def on_scene_item_transform_changed(data):

466

"""Handle scene item transform changes (frequent during dragging)."""

467

global transform_updates

468

transform_updates += 1

469

470

if transform_updates % 10 == 0:

471

print(f"Transform updates: {transform_updates} (Item {data.scene_item_id})")

472

473

def on_input_active_state_changed(data):

474

"""Handle input active state changes."""

475

state = "active" if data.input_active else "inactive"

476

print(f"Input '{data.input_name}' is now {state}")

477

478

client.callback.register([

479

on_input_volume_meters,

480

on_scene_item_transform_changed,

481

on_input_active_state_changed

482

])

483

484

print("Monitoring high-volume events...")

485

print("Warning: This will generate many events!")

486

487

try:

488

# Run for 30 seconds

489

time.sleep(30)

490

finally:

491

client.disconnect()

492

print(f"Final counts - Volume: {volume_updates}, Transform: {transform_updates}")

493

```

494

495

### Dynamic Event Registration

496

497

```python

498

import obsws_python as obs

499

500

class OBSEventManager:

501

def __init__(self):

502

self.client = obs.EventClient(subs=obs.Subs.LOW_VOLUME)

503

self.handlers = {}

504

505

def add_scene_monitoring(self):

506

"""Add scene-related event handlers."""

507

def on_scene_created(data):

508

print(f"📄 Scene created: {data.scene_name}")

509

510

def on_current_program_scene_changed(data):

511

print(f"🎬 Now showing: {data.scene_name}")

512

513

self.handlers['scenes'] = [on_scene_created, on_current_program_scene_changed]

514

self.client.callback.register(self.handlers['scenes'])

515

print("Scene monitoring enabled")

516

517

def add_input_monitoring(self):

518

"""Add input-related event handlers."""

519

def on_input_mute_state_changed(data):

520

emoji = "🔇" if data.input_muted else "🔊"

521

print(f"{emoji} {data.input_name} mute: {data.input_muted}")

522

523

def on_input_volume_changed(data):

524

print(f"🎚️ {data.input_name} volume: {data.input_volume_db:.1f}dB")

525

526

self.handlers['inputs'] = [on_input_mute_state_changed, on_input_volume_changed]

527

self.client.callback.register(self.handlers['inputs'])

528

print("Input monitoring enabled")

529

530

def remove_monitoring(self, category):

531

"""Remove specific category of event handlers."""

532

if category in self.handlers:

533

self.client.callback.deregister(self.handlers[category])

534

del self.handlers[category]

535

print(f"{category} monitoring disabled")

536

537

def list_active_events(self):

538

"""Show currently registered events."""

539

events = self.client.callback.get()

540

if events:

541

print("Active events:")

542

for event in events:

543

print(f" - {event}")

544

else:

545

print("No events registered")

546

547

def shutdown(self):

548

"""Clean shutdown."""

549

self.client.disconnect()

550

551

# Usage

552

manager = OBSEventManager()

553

554

try:

555

# Start with scene monitoring

556

manager.add_scene_monitoring()

557

manager.list_active_events()

558

559

input("Press Enter to add input monitoring...\n")

560

manager.add_input_monitoring()

561

manager.list_active_events()

562

563

input("Press Enter to remove scene monitoring...\n")

564

manager.remove_monitoring('scenes')

565

manager.list_active_events()

566

567

input("Press Enter to exit...\n")

568

569

finally:

570

manager.shutdown()

571

```