Skip to content

DataSource System

window.lcards.core.dataSourceManager — Named entity data buffers with history and processing pipelines.


Overview

DataSourceManager extends BaseService and manages a collection of named DataSource instances. Each source subscribes to a single HA entity, records a rolling value history, runs processor pipelines, and notifies subscribers on every update.

Cards never talk to HA entity state directly for data — they declare data sources in config and subscribe to the manager.


Key Classes

ClassFileRole
DataSourceManagercore/data-sources/DataSourceManager.jsLifecycle, card registration, entity index
DataSourcecore/data-sources/DataSource.jsSingle entity subscription, buffer, processor chain
RollingBuffercore/data-sources/RollingBuffer.jsFixed-size circular value history
ProcessorManagercore/data-sources/ProcessorManager.jsRuns processor chain; writes keyed output buffers

Data Object Structure

Every subscriber callback receives:

javascript
{
  v: 72.5,             // Raw entity state value (main buffer)
  celsius: 22.5,       // Processor output keyed by processor's `key`
  rolling_avg: 71.8,   // Another processor output
  t: 1707580800000     // Timestamp (ms)
}

Config Schema

yaml
data_sources:
  temp_sensor:
    entity_id: sensor.temperature
    update_interval: 5          # polling interval (seconds); 0 = push-only
    history_size: 100           # rolling buffer depth
    processors:
      - type: unit_conversion
        key: celsius
        from_unit: fahrenheit
        to_unit: celsius
      - type: smooth
        key: rolling_avg
        window: 10

Processors Reference

TypeKey fieldOutput
convert_unitcustomConverted numeric value
smoothcustomSmoothed value (exponential, moving average, or gaussian)
ratecustomRate of change per second
deltacustomDifference from previous value
roundcustomRounded to N decimal places
scalecustomLinear scale (min→max mapping)
clampcustomValue clamped to [min, max]
thresholdcustomBoolean: value above threshold
trendcustom"rising" / "falling" / "stable"
statisticscustom{ min, max, avg, stddev } object
durationcustomDuration entity formatted string
expressioncustomArbitrary JS expression result

Processor Execution Order & Chaining

Processors run in dependency order, not config order. ProcessorManager performs a topological sort (Kahn's algorithm) at initialization. The resolved execution order is logged at debug level on startup.

Each processor declares its input via input_source:

  • No input_source: processor reads from the raw entity value pushed by HA
  • input_source: other_key: processor reads from the output of the named processor

A processor only ever sees one input — either the raw entity value or the output of its input_source target. Processors with no shared dependencies run in an unspecified order relative to each other; only declared chains are ordered.

Circular dependencies throw at initialization — the DataSource will fail to configure.

yaml
data_sources:
  temp_sensor:
    entity_id: sensor.outdoor_temp
    processors:
      # Step 1 — reads raw entity value (no input_source)
      celsius:
        type: convert_unit
        from_unit: °F
        to_unit: °C

      # Step 2 — reads output of 'celsius' processor
      smoothed:
        type: smooth
        input_source: celsius
        method: moving_average
        window: 5

      # Step 3 — reads output of 'smoothed'
      display:
        type: round
        input_source: smoothed
        decimals: 1

Access each stage in templates: {ds:temp_sensor.celsius}, {ds:temp_sensor.smoothed}, {ds:temp_sensor.display}.


Card Usage

javascript
// In _handleFirstUpdate():
const dsm = window.lcards.core.dataSourceManager;
await dsm.initializeFromConfig(this.config.data_sources || {});

const source = dsm.getSource('temp_sensor');
this._unsubscribe = source.subscribe((data) => {
  this._temp = data.celsius;
  this.requestUpdate();
});

// In disconnectedCallback():
if (this._unsubscribe) this._unsubscribe();

Template Access

yaml
text: "{ds:temp_sensor}"                   # HA-native: locale-formatted + unit
text: "{ds:temp_sensor.celsius:.1f}"       # processor buffer: 1 decimal, no auto-unit
text: "{ds:temp_sensor.celsius:.1f} °C"   # explicit unit suffix
text: "{datasource:temp_sensor.rolling_avg}"  # explicit prefix, HA-native

No format spec → HA-native (locale + unit). With format spec → number only, you control the suffix.


Public API

Two levels: the DataSourceManager singleton and individual DataSource instances.

DataSourceManager

Property / MethodReturnsDescription
sourcesMap<name, DataSource>All active DataSource instances
getSource(name)DataSource|nullDataSource by its config key name
initializeFromConfig(dsConfig)Promise<void>Register and start all DataSources from a card config block

DataSource instance

MethodReturnsDescription
subscribe(cb)() => voidSubscribe to value updates { v, t }; returns unsubscribe fn
getValue()anyCurrent value (synchronous)
getHistory()Object[]Recent value buffer [{ v, t }, ...]

Console Access

javascript
window.lcards.debug.singleton('dataSourceManager')
// → { type: 'DataSourceManager', sourceCount: 4, sources: [...] }
javascript
const dsm = window.lcards.core.dataSourceManager

dsm.sources                           // Map of all active DataSource instances
dsm.getSource('sensor_temp')          // specific DataSource instance
dsm.getSource('sensor_temp').subscribe(cb)  // subscribe to updates
dsm.getSource('sensor_temp').getHistory()   // recent value history

Debug Namespace (window.lcards.debug.datasources)

Specialized introspection tools for deep DataSource analysis — processor graphs, validation, buffer stats.

TIP

javascript
window.lcards.debug.datasources.help()   // print method summary
window.lcards.debug.datasources.list()   // list all active source names
MethodDescription
list()All active DataSource names
get(name)DataSource instance by name
listProcessors(dsName)Processor keys registered on a DataSource
showProcessorGraph(dsName)Log processor execution order; returns { nodes, edges }
inspectProcessor(dsName, processorName)Config, currentValue, buffer size, and stats for one processor
validate(dsName)Config validation report { valid, errors, warnings, info }
getStats(dsName)Full stats: entity, buffer { size, capacity, oldest, newest }, processing
javascript
const ds = window.lcards.debug.datasources

ds.list()                                            // ['sensor_temp', 'cpu_usage', ...]
ds.validate('sensor_temp')                           // { valid: true, errors: [], warnings: [] }
ds.getStats('sensor_temp')                           // { entity, buffer: { size, capacity, ... } }
ds.listProcessors('sensor_temp')                     // ['moving_average', 'rate_limiter', ...]
ds.inspectProcessor('sensor_temp', 'moving_average') // { config, currentValue, bufferSize, stats }
ds.showProcessorGraph('sensor_temp')                 // logs + returns { nodes, edges }

See Also