Configuration Management – Making Device Parameters Accessible

Beyond Static Configuration Files

Traditional applications often manage configuration through static files—JSON documents, environment variables, or database tables that require manual updates and application restarts to take effect. This approach works well for abstract software settings, but it falls short when configuration needs to control real-time hardware behavior and adapt to changing operational conditions.

Imagine trying to adjust the brewing temperature on a coffee machine by editing a configuration file and restarting the application. By the time the change takes effect, dozens of customers might have received improperly brewed beverages. Physical systems require configuration that can be updated immediately and reflected in real-time operation.

KOS configuration properties bridge this gap by creating live connections between your application interface and the operational parameters that control device behavior. When operators adjust settings through your UI, those changes immediately propagate to the hardware systems that implement them.

Configuration as Living System State

The Traditional Configuration Problem

Most applications treat configuration as a separate concern from application state. You have your runtime data (user interactions, current operations, temporary values) and your configuration data (settings that control how the application behaves). These two worlds rarely intersect cleanly.

This separation becomes problematic when configuration changes need to trigger immediate system responses. If a operator adjusts a pump flow rate, the pump hardware needs to adopt the new rate immediately, not after the next application restart.

KOS Configuration Properties: Reactive Parameters

KOS treats configuration as reactive system state that automatically synchronizes between your user interface, your application logic, and the physical device parameters they control.

import { kosConfigProperty, KosConfigProperty, kosModel, IKosDataModel } from "@kosdev-code/kos-ui-sdk";
@kosModel({ modelTypeId: "temperature-control-model", singleton: false })
export class TemperatureControlModelImpl implements IKosDataModel {
  // Live connection to device temperature setting
  @kosConfigProperty({
    path: "assembly:core:board:cooling",
    attribute: "targetTemperature",
  })
  targetTemperature!: KosConfigProperty<number>;
  // Access current configured value
  get currentTarget(): number {
    return this.targetTemperature.value || 22; // Default to 22°C
  }
  // Update configuration with immediate effect
  adjustTemperature(newTarget: number): void {
    this.targetTemperature.updateProperty(newTarget);
    // Device hardware receives new temperature target immediately
  }
  // Automatic response to configuration changes
  @kosModelEffect({
    dependencies: (model) => [model.targetTemperature.value],
  })
  handleTemperatureConfigChange(): void {
    const newTarget = this.targetTemperature.value;
    if (newTarget !== undefined) {
      this.logger.info(`Temperature target updated to ${newTarget}°C`);
      // Trigger any business logic that depends on temperature settings
      this.recalculateOperatingParameters();
    }
  }
}

This creates a direct connection between user interface controls, application logic, and device behavior. When someone adjusts the temperature setting, the change flows immediately through the entire system without requiring manual refresh operations or system restarts.

Understanding Configuration Hierarchies

KOS configuration is organized in hierarchical paths that mirror the physical and logical structure of your device systems. This organization makes configuration intuitive—settings that control related functionality are grouped together in logical ways.

System-Level Configuration

At the highest level, system-wide settings control broad operational behavior:

@kosModel({ modelTypeId: "system-manager-model", singleton: true })
export class SystemManagerModelImpl implements IKosDataModel {
  // Machine identification and basic settings
  @kosConfigProperty({
    path: "system:app",
    attribute: "machineId",
  })
  machineId!: KosConfigProperty<string>;
  @kosConfigProperty({
    path: "system:app", 
    attribute: "operatingMode",
  })
  operatingMode!: KosConfigProperty<string>;
  // Service-level configuration  
  @kosConfigProperty({
    path: "kos:service:region",
    attribute: "unitSystem",
  })
  unitSystem!: KosConfigProperty<string>;
  get machineIdentifier(): string {
    return this.machineId.value || "unknown-machine";
  }
  get isMetricSystem(): boolean {
    return this.unitSystem.value === "metric";
  }
}

System-level configuration typically includes machine identity, operational modes, regional settings, and other parameters that affect overall system behavior.

Component-Level Configuration

Hardware components have their own configuration spaces that control their specific operational parameters:

@kosModel({ modelTypeId: "pump-controller-model", singleton: true })
export class PumpControllerModelImpl implements IKosDataModel {
  // Specific pump component configuration
  @kosConfigProperty({
    path: "assembly:core:board:macro:pump:water",
    attribute: "flowRate",
  })
  waterPumpFlowRate!: KosConfigProperty<number>;
  @kosConfigProperty({
    path: "assembly:core:board:micro:pump:S4",
    attribute: "calibrationOffset",
  })
  syrupPumpOffset!: KosConfigProperty<number>;
  // Ice system configuration
  @kosConfigProperty({
    path: "assembly:core:board:macro:iceAgitator", 
    attribute: "settings.inactivitySec",
  })
  iceAgitatorTimeout!: KosConfigProperty<number>;
  get effectiveFlowRate(): number {
    return this.waterPumpFlowRate.value || 100;
  }
  get agitatorTimeoutMinutes(): number {
    const seconds = this.iceAgitatorTimeout.value || 300;
    return seconds / 60;
  }
}

Component-level configuration provides fine-grained control over individual hardware elements while maintaining clear organization through the hierarchical path structure.

Automatic Unit Conversion and Regional Adaptation

One of the most sophisticated aspects of KOS configuration properties is their ability to automatically handle unit conversions and regional preferences without requiring manual developer intervention.

Regional Unit System Support

The same physical parameter can be displayed and modified using different unit systems depending on where the device is deployed:

@kosModel({ modelTypeId: "beverage-dispenser-model", singleton: false })
export class BeverageDispenserModelImpl implements IKosDataModel {
  // Backend stores volume in liters (SI unit)
  @kosConfigProperty({
    path: "dispensing:settings",
    attribute: "maxVolume"
  })
  maxVolume!: KosConfigProperty<number>;
  // Framework provides multiple representations
  get volumeInfo() {
    return {
      value: this.maxVolume.value,        // Region-appropriate: 16.9 (US) or 500 (EU)
      displayValue: this.maxVolume.displayValue, // Formatted: "16.9 fl oz" or "500 ml"
      unit: this.maxVolume.unit,          // Regional unit: "fl oz" or "ml"
      rawValue: this.maxVolume.rawValue,  // SI backend value: 0.5 (liters)
    };
  }
  // Temperature with automatic regional conversion
  @kosConfigProperty({
    path: "assembly:core:board:cooling",
    attribute: "targetTemperature"
  })
  targetTemperature!: KosConfigProperty<number>;
  get temperatureDisplay(): string {
    // Automatically displays as Fahrenheit in US, Celsius in EU
    return this.targetTemperature.displayValue || "Not set";
  }
}

This automatic conversion means your application can work seamlessly in different regions without requiring separate implementations or manual unit handling code.

Custom Unit Conversion When Needed

Most configurations work perfectly with automatic conversion, but some scenarios require specific unit display:

@kosModel({ modelTypeId: "technical-diagnostics-model", singleton: false })
export class TechnicalDiagnosticsModelImpl implements IKosDataModel {
  // Force temperature display in Celsius for technical readings
  @kosConfigProperty({
    path: "assembly:sensors",
    attribute: "calibrationTemperature",
    converter: {
      to: { unit: "celsius" }, // Always show technical data in Celsius
    },
  })
  calibrationTemp!: KosConfigProperty<number>;
  // Convert duration from seconds to minutes for operator interface
  @kosConfigProperty({
    path: "system:timers",
    attribute: "cleaningCycleDuration",
    converter: {
      to: { unit: "minute" }, // Show duration in minutes instead of seconds
    },
  })
  cleaningDuration!: KosConfigProperty<number>;
  get calibrationStatus(): string {
    const temp = this.calibrationTemp.value;
    return temp ? `Calibrated at ${temp}°C` : "Not calibrated";
  }
  get cleaningSchedule(): string {
    const minutes = this.cleaningDuration.value;
    return minutes ? `${minutes} minute cycles` : "No schedule set";
  }
}

This flexibility allows you to provide both user-friendly regional display and technically precise measurements where needed.

Configuration with Dynamic Options

Many configuration parameters don’t accept arbitrary values—they need to be selected from specific options, validated against constraints, or provide guidance about acceptable ranges.

Option Lists and Validation

@kosModel({ modelTypeId: "product-selection-model", singleton: false })
export class ProductSelectionModelImpl implements IKosDataModel {
  // Configuration with predefined options
  @kosConfigProperty({
    path: "app:products",
    attribute: "selectedBrand",
    optionsExpander: [
      { label: "Coca-Cola Classic", value: "coke-classic" },
      { label: "Diet Coke", value: "diet-coke" },
      { label: "Sprite", value: "sprite" },
      { label: "Fanta Orange", value: "fanta-orange" },
    ],
  })
  selectedBrand!: KosConfigProperty<string>;
  // Numeric configuration with formatting
  @kosConfigProperty({
    path: "app:dispensing",
    attribute: "volume",
    formatter: {
      style: "unit",
      maximumFractionDigits: 0,
    },
  })
  dispensingVolume!: KosConfigProperty<number>;
  get availableBrands() {
    return this.selectedBrand.optionsExpander || [];
  }
  get currentBrandLabel(): string {
    const current = this.selectedBrand.value;
    const option = this.availableBrands.find(opt => opt.value === current);
    return option?.label || "No brand selected";
  }
  get isValidVolume(): boolean {
    const volume = this.dispensingVolume.value;
    return volume !== undefined && volume > 0 && volume <= 500;
  }
}

This approach provides guided configuration that prevents invalid settings while still allowing operational flexibility.

Nested Configuration Properties

Complex configuration often involves nested object structures that group related parameters:

@kosModel({ modelTypeId: "scheduling-model", singleton: false })
export class SchedulingModelImpl implements IKosDataModel {
  // Access nested configuration properties
  @kosConfigProperty({
    path: "app:scheduling",
    attribute: "dayWiseLockConfigMap.Mon.enabled",
  })
  mondayLockEnabled!: KosConfigProperty<boolean>;
  @kosConfigProperty({
    path: "app:scheduling",
    attribute: "dayWiseLockConfigMap.Mon.startTime",
  })
  mondayLockStart!: KosConfigProperty<string>;
  @kosConfigProperty({
    path: "app:scheduling", 
    attribute: "dayWiseLockConfigMap.Mon.endTime",
  })
  mondayLockEnd!: KosConfigProperty<string>;
  get mondaySchedule(): ScheduleInfo {
    return {
      enabled: this.mondayLockEnabled.value || false,
      startTime: this.mondayLockStart.value || "09:00",
      endTime: this.mondayLockEnd.value || "17:00",
    };
  }
  updateMondaySchedule(schedule: ScheduleInfo): void {
    this.mondayLockEnabled.updateProperty(schedule.enabled);
    this.mondayLockStart.updateProperty(schedule.startTime);
    this.mondayLockEnd.updateProperty(schedule.endTime);
  }
}

Nested configuration allows logical grouping of related parameters while maintaining individual control over each setting.

Real-Time Configuration Updates

Configuration properties automatically synchronize changes across your entire application ecosystem. When configuration changes occur—whether through your user interface, administrative tools, or backend systems—all connected applications receive updates immediately.

Reactive Configuration Responses

Your models can respond automatically to configuration changes using model effects:

@kosModel({ modelTypeId: "brewing-system-model", singleton: true })
export class BrewingSystemModelImpl implements IKosDataModel {
  @kosConfigProperty({
    path: "brewing:temperature",
    attribute: "targetTemp",
  })
  brewingTemperature!: KosConfigProperty<number>;
  @kosConfigProperty({
    path: "brewing:timing",
    attribute: "brewDuration",
  })
  brewDuration!: KosConfigProperty<number>;
  // Respond automatically to brewing temperature changes
  @kosModelEffect({
    dependencies: (model) => [model.brewingTemperature.value],
  })
  handleTemperatureConfigChange(): void {
    const newTemp = this.brewingTemperature.value;
    if (newTemp !== undefined) {
      this.logger.info(`Brewing temperature updated to ${newTemp}°C`);
      // Adjust heating system immediately
      this.adjustHeatingTarget(newTemp);
      // Recalculate brewing parameters based on new temperature
      this.updateBrewingParameters();
      // Validate that new temperature is within safe operating range
      this.validateTemperatureSafety(newTemp);
    }
  }
  // Respond to brew duration changes
  @kosModelEffect({
    dependencies: (model) => [model.brewDuration.value],
  })
  handleDurationConfigChange(): void {
    const newDuration = this.brewDuration.value;
    if (newDuration !== undefined) {
      this.logger.info(`Brew duration updated to ${newDuration} seconds`);
      // Update timing controllers
      this.updateBrewingTimers(newDuration);
      // Adjust other parameters that depend on brew duration
      this.recalculateExtractionProfile(newDuration);
    }
  }
}

This reactive pattern ensures that configuration changes immediately propagate through all the business logic that depends on them, creating systems that respond intelligently to operational adjustments.

Multi-Model Configuration Coordination

Configuration changes often affect multiple models that need to coordinate their responses:

@kosModel({ modelTypeId: "system-coordinator-model", singleton: true })
export class SystemCoordinatorModelImpl implements IKosDataModel {
  @kosDependency({ modelType: TemperatureControlModel.type })
  temperatureControl!: TemperatureControlModel;
  @kosDependency({ modelType: PumpControllerModel.type })
  pumpController!: PumpControllerModel;
  @kosConfigProperty({
    path: "system:app",
    attribute: "operatingMode",
  })
  operatingMode!: KosConfigProperty<string>;
  // Coordinate system-wide configuration changes
  @kosModelEffect({
    dependencies: (model) => [model.operatingMode.value],
  })
  handleOperatingModeChange(): void {
    const mode = this.operatingMode.value;
    if (mode === "maintenance") {
      // Coordinate maintenance mode across all subsystems
      this.temperatureControl.enterMaintenanceMode();
      this.pumpController.disableAllPumps();
      this.showMaintenanceInterface();
    } else if (mode === "normal") {
      // Return all systems to normal operation
      this.temperatureControl.resumeNormalOperation();
      this.pumpController.enableConfiguredPumps();
      this.showOperationalInterface();
    }
    this.logger.info(`System operating mode changed to: ${mode}`);
  }
}

This coordination ensures that configuration changes create consistent system-wide behavior rather than isolated parameter adjustments.

Integration with User Interfaces

Configuration properties integrate seamlessly with React components to create intuitive configuration interfaces:

// React component for temperature configuration
const TemperatureConfigPanel: React.FC = () => {
  const { model } = useTemperatureControlModel();
  return (
    <div className="temperature-config">
      <h3>Temperature Settings</h3>
      <div className="config-row">
        <label>Target Temperature</label>
        <input
          type="number"
          value={model.targetTemperature.value || 0}
          onChange={(e) => 
            model.targetTemperature.updateProperty(Number(e.target.value))
          }
          step={0.5}
          min={0}
          max={100}
        />
        <span className="unit">{model.targetTemperature.unit}</span>
      </div>
      <div className="display-value">
        Current Setting: {model.targetTemperature.displayValue}
      </div>
      <div className="raw-value">
        Backend Value: {model.targetTemperature.rawValue} 
        (stored in SI units)
      </div>
    </div>
  );
};

The reactive nature of configuration properties means these interfaces automatically update when configuration changes occur through any means—direct user input, administrative tools, or programmatic updates.

Configuration Best Practices

Provide Sensible Defaults

Always provide fallback values for configuration properties to ensure your application functions even when configuration is incomplete:

@kosModel({ modelTypeId: "dispenser-model", singleton: false })
export class DispenserModelImpl implements IKosDataModel {
  @kosConfigProperty({
    path: "dispensing:flow",
    attribute: "rate",
  })
  flowRate!: KosConfigProperty<number>;
  // Always provide a sensible default
  get effectiveFlowRate(): number {
    return this.flowRate.value ?? 100; // Default to 100 ml/min
  }
  // Validate configuration before using it
  get isFlowRateValid(): boolean {
    const rate = this.flowRate.value;
    return rate !== undefined && rate > 0 && rate <= 500;
  }
}

Use Specific Configuration Paths

Organize configuration using clear, specific paths that reflect your system architecture:

// Good: Specific, hierarchical paths
@kosConfigProperty({ 
  path: "assembly:core:board:cooling", 
  attribute: "targetTemperature" 
})
@kosConfigProperty({ 
  path: "app:user-preferences", 
  attribute: "defaultLanguage" 
})
// Avoid: Generic, ambiguous paths
@kosConfigProperty({ 
  path: "config", 
  attribute: "temp" 
})

Handle Configuration Updates Gracefully

Always include error handling when responding to configuration changes:

@kosModelEffect({
  dependencies: (model) => [model.configProperty.value],
})
handleConfigChange(): void {
  try {
    const newValue = this.configProperty.value;
    if (newValue !== undefined) {
      this.applyConfigurationSafely(newValue);
    }
  } catch (error) {
    this.logger.error("Failed to apply configuration change", error);
    // Potentially revert to previous safe value
    this.revertToSafeConfiguration();
  }
}

Building Responsive Configuration Systems

KOS configuration properties transform static parameter management into dynamic, responsive systems that bridge the gap between user interfaces and physical device behavior. By treating configuration as reactive system state, you create applications where operational adjustments flow seamlessly from user intent through application logic to hardware implementation.

This approach eliminates the disconnect between configuration interfaces and actual device behavior, creating systems that feel responsive and trustworthy because changes take effect immediately and visibly.


Ready to explore more advanced KOS patterns? Continue with Futures and Long-Running Operations to learn how KOS handles complex device procedures, or dive into Trouble System Integration for comprehensive device health management.