Receiving Device Events with Topic Handlers

What You’ll Build

Extend your color manager model to receive real-time color change events from the device. You’ll combine the baseline color (from tutorial 03’s service request) with live updates via @kosTopicHandler, demonstrating how KOS models integrate both API requests and event streams.

Prerequisites

  • Completed the “Making Service Requests” tutorial
  • Color manager model with service request working
  • Development server running (npm run dev, kosui dev, or npx nx serve my-first-kos-app-ui)
  • Chrome DevTools Console tab open

Understanding Topics

KOS devices publish events over WebSocket to “topics”—named channels that models can subscribe to. When an event arrives on a topic, the framework automatically routes it to any handler methods decorated with @kosTopicHandler.

Common pattern:

  • Service request loads baseline/initial data (like the baseline color)
  • Topic handler receives real-time updates as data changes on the device

For this tutorial, we’ll listen for /kos/colors/changed events.

Step 1: Add Topic Handler Import

Open libs/my-first-kos-app-models/src/models/color-manager/color-manager-model.ts.

Add kosTopicHandler to your imports:

import {
  kosModel,
  IKosDataModel,
  kosLoggerAware,
  KosLoggerAware,
  kosTopicHandler,  // Add this
} from "@kosdev-code/kos-ui-sdk";

[Screenshot: VS Code showing the import statement with kosTopicHandler highlighted]

Step 2: Add Topic Handler Method

Add a topic handler method after your service request handler:

@kosModel({ modelTypeId: MODEL_TYPE, singleton: true })
@kosLoggerAware()
export class ColorManagerModelImpl implements IKosDataModel {
  currentColor: string = '#000000';
  @kosServiceRequest({
    lifecycle: DependencyLifecycle.READY,
    servicePath: PATH_BASELINE_COLOR,
    serviceFunction: getBaselineColor,
  })
  async handleBaselineColorLoaded([error, response]: ApiServiceResponse): Promise<void> {
    if (error) {
      this.logger.error('Failed to load baseline color:', error);
      return;
    }
    this.logger.info('Baseline color received:', response);
    this.currentColor = response.color;
    this.logger.info(`Color updated to: ${this.currentColor}`);
  }
  // Add this new topic handler
  @kosTopicHandler({ topic: '/kos/colors/changed', websocket: true })
  handleColorChanged(payload: { color: string }): void {
    this.logger.info('Color change event received:', payload);
    this.currentColor = payload.color;
    this.logger.info(`Color updated to: ${this.currentColor}`);
  }
  async init(): Promise<void> {
    this.logger.info('Color manager initialized');
  }
  async load(): Promise<void> {
    this.logger.info('Color manager loaded');
  }
}

[Screenshot: VS Code showing the complete model with both the service request handler and the new topic handler]

What This Does

The @kosTopicHandler decorator tells the KOS framework to:

  • Subscribe to the /kos/colors/changed topic when the model initializes
  • Route any events on that topic to the handleColorChanged method
  • Unsubscribe automatically when the model is destroyed

The websocket: true flag indicates this handler listens for events from the device backend over WebSocket, distinguishing between:

  • WebSocket events (websocket: true) – Published by the device backend
  • Internal events (websocket: false or omitted) – Published within the application using EventBus.publish()

The framework handles all WebSocket connection management, subscription lifecycle, and error handling.

Step 3: Simulate Events for Testing

Since we don’t have a real device publishing color events yet, let’s simulate them using the EventBus.

Add the EventBus import:

import {
  kosModel,
  IKosDataModel,
  kosLoggerAware,
  KosLoggerAware,
  kosTopicHandler,
  EventBus,  // Add this
} from "@kosdev-code/kos-ui-sdk";

Update your init() method to publish test events:

async init(): Promise<void> {
  this.logger.info('Color manager initialized');
  // Simulate device color change events for testing
  const colors = ['#FF5733', '#33FF57', '#3357FF', '#FF33F5', '#F5FF33', '#33FFF5'];
  let colorIndex = 0;
  setInterval(() => {
    const testPayload = {
      color: colors[colorIndex % colors.length],
      timestamp: Date.now()
    };
    // Publish to the topic we're listening to
    EventBus.publish('/kos/colors/changed', testPayload);
    colorIndex++;
  }, 3000); // Change color every 3 seconds
}

[Screenshot: VS Code showing the updated init() method with the setInterval code]

Why: This simulates what a real device would do—publish color change events over WebSocket. In production, these events would come from the device backend, but for development, we can test the handler with simulated events.

Step 4: See It Work

Save all files. The browser automatically refreshes.

What happens:

  1. App starts – Color manager model is created
  2. Model initializes – Service request fetches baseline color
  3. Baseline loads – currentColor updates to the API response color (e.g., #FF5733)
  4. Simulated events start – Every 3 seconds, a new color is published
  5. Handler fires – handleColorChanged receives the event and updates currentColor
  6. UI reacts – ColorDisplay component re-renders with new background color

[Screenshot: Browser showing the color display changing colors automatically, with both the counter and color components visible]

You’ll see the color box changing colors every 3 seconds, cycling through the array of test colors.

Open the Console (F12) and you’ll see:

Color manager initialized
Color manager loaded
Baseline color received: { color: "#FF5733" }
Color updated to: #FF5733
Color change event received: { color: "#33FF57", timestamp: 1234567890 }
Color updated to: #33FF57
Color change event received: { color: "#3357FF", timestamp: 1234567893 }
Color updated to: #3357FF
...

[Screenshot: Chrome DevTools Console showing the model lifecycle, service request, and streaming event logs]

What Just Happened

You built a model that combines two data sources:

  1. Service Request (@kosServiceRequest) – Fetches initial/baseline color from API when model becomes ready
  2. Topic Handler (@kosTopicHandler) – Listens for real-time color change events and updates the color continuously

Both decorators update the same observable property (currentColor), and the UI automatically reacts to changes from either source. This is a fundamental KOS pattern:

  • Lifecycle requests for baseline data
  • Topic handlers for real-time updates
  • Observable properties for automatic UI synchronization

Key Concepts Demonstrated

Event-Driven Architecture: The @kosTopicHandler decorator subscribes to device event topics. When the device publishes an event, your handler method is called automatically.

Combined Data Sources: A single model can integrate multiple data sources—API requests for baseline data and WebSocket events for live updates—all updating the same observable properties.

Automatic Subscription Management: You don’t manually subscribe/unsubscribe from topics. The decorator handles the entire subscription lifecycle based on model lifecycle.

Type-Safe Event Payloads: The payload: { color: string } parameter ensures type safety for incoming events.

Zero Boilerplate: No WebSocket connection code, no subscription management, no event routing logic—the decorator handles everything.

Understanding Real-World Usage

In production KOS applications, the EventBus.publish() simulation would be replaced by actual device events:

Real Device Scenario:

  1. User changes a setting on the device (or a sensor detects a change)
  2. Device backend publishes an event to /kos/colors/changed via WebSocket
  3. Studio (or the deployed UI) receives the WebSocket message
  4. KOS framework routes it to your handleColorChanged method
  5. Your model updates, UI reacts

Why simulate with EventBus?

  • Test handlers during development without requiring a running device
  • Verify handler logic before device integration
  • Develop UI before backend events are available

Finding Real Topics

In real development, you’ll need to discover what topics your device publishes:

1. Review Design Approach Documents – Check for established frontend-backend contracts that document topic names and payload structures

2. Collaborate with the Java/Backend Team – Have a conversation with backend developers to understand what topics are available and what events they publish

3. Use KOS Studio WebSocket Viewer – Studio’s WebSocket Viewer tool is customized for KOS events and provides:

  • Clear visualization of all WebSocket traffic
  • Request/response reconciliation
  • Topic filtering and search
  • Payload inspection with syntax highlighting
  • Better handling of KOS-specific event formats than generic Chrome DevTools

4. Check OpenAPI Documentation – Some devices document their WebSocket topics in OpenAPI specs or separate event catalogs

Multiple Topic Handlers

You can add multiple topic handlers to a single model:

@kosTopicHandler({ topic: '/kos/colors/changed', websocket: true })
handleColorChanged(payload: { color: string }): void {
  this.currentColor = payload.color;
  this.logger.info(`Color updated to: ${this.currentColor}`);
}
@kosTopicHandler({ topic: '/kos/colors/reset', websocket: true })
handleColorReset(): void {
  this.currentColor = '#000000';
  this.logger.info('Color reset to default');
}
@kosTopicHandler({ topic: '/kos/colors/paletteChanged', websocket: true })
handlePaletteChanged(payload: { colors: string[] }): void {
  this.logger.info('Color palette updated:', payload.colors);
  // Update color palette in model
}

Each handler subscribes to its own topic automatically when the model initializes.

Try This

Enhance your color manager:

  1. Add a colorHistory: string[] property to track all colors received
  2. In handleColorChanged, push each new color to the history array
  3. Update ColorDisplay to show the last 5 colors in the history
  4. Add a @kosTopicHandler for a /kos/colors/reset topic that clears the history

You’ll see that adding observable properties and handlers requires no changes to React state management—just add to the model, and components automatically update.

Next Steps

Now that you understand both service requests and topic handlers, you can:

  • Combine patterns – Use service requests for baseline data, topic handlers for updates
  • Add configuration – Read device settings with @kosConfigProperty
  • Build complex models – Models that integrate multiple APIs and event streams
  • Handle device state – Track device status, sensor readings, and operational events

Next: Learn how to read and write device configuration with @kosConfigProperty.