pamoja for TypeScript - v0.2.0
    Preparing search index...

    Module @pamoja/profile

    Ergonomic facade over the generated device-profile binding.

    A profile is a named, pre-wired bundle: a control policy, a publish topic, and a power schedule. Instantiate one rather than choosing algorithms and tuning constants by hand.

    A profile is the manifest, which loads from and saves to JSON so it ships as a file. A controller is the decision logic that manifest describes: hand it a reading and it says what the output should do and whether the reading crossed a threshold worth raising. The presentation a dashboard reads travels inside the manifest JSON and is read and built here as a typed Presentation.

    A Node runs a profile: each tick reads, lets the profile's policy decide, switches the output, and publishes the reading over a link, and run repeats that at the cadence the battery allows. A PolicyRegistry resolves a control kind the library never shipped to the program's own code.

    A rule file decides between nodes: a RuleEngine runs it off a link, switching outputs by name and publishing as rules set and clear, and RuleEvaluator is its deciding half for a program that moves its own messages.

    The alert, control, and rule action kinds are re-exported as runtime objects, because the generated enums are types-only.

    @pamoja/profile

    Named, ready-to-run device profiles from plain data or a JSON manifest, run as a node in every language. One capability of pamoja, one memory-safe Rust core with bindings for TypeScript, Python, and C#.

    read the guide documentation API reference

    npm install @pamoja/profile
    

    This pulls in @pamoja/native, the compiled engine. npm install pamoja is the whole framework in one package.

    The test that runs in CI, spliced here as it ran.

    From bindings/node/guides/profile.ts:

    import { readFileSync } from 'node:fs'
    import { LoopbackBroker } from '@pamoja/loopback'
    import { Node, Profile } from '@pamoja/profile'

    // A profile is a file. This one ships in the catalog under profiles/: it holds a brooder at
    // 32 C by switching a heat lamp, says what it reads, and says how a dashboard draws it.
    const text = readFileSync('profiles/brooder-heater.json', 'utf8')
    const profile = Profile.fromJson(text)

    async function example(): Promise<boolean> {
    const reads = profile.reads!
    console.log(`profile ${profile.name} reads ${reads.quantity} in ${reads.unit} and reports on ${profile.topic}`)

    // A node is the profile and the parts that make it run: a sensor, an output, and a link.
    // A morning of readings stands in for the probe, and a dashboard listens on the same
    // broker.
    const broker = new LoopbackBroker()
    const link = broker.link()
    const dashboard = broker.link()
    await link.connect()
    await dashboard.connect()
    await dashboard.subscribe(profile.topic)
    const morning = [27.5, 31.8, 32.6, 32.1, 31.4]
    let lamp = false
    const node = new Node({
    profile,
    read: () => morning.shift()!,
    drive: (on) => {
    lamp = on
    },
    link,
    })

    // Each tick reads, decides, switches the lamp, and publishes the reading. The lamp comes on
    // at 31.5 C or below and goes off at 32.5 C or above, and in between it stays as it was; a
    // reading more than 4 C from 32 raises an alert as well.
    let was = false
    for (let at = 0; at < 5; at += 1) {
    const { reading, reaction } = await node.tick()
    const on = reaction.actuator === true
    const change = on ? (was ? 'lamp stays on' : 'lamp on') : was ? 'lamp off' : 'lamp stays off'
    const alert = reaction.alert ? `, alert ${reaction.alert.kind}` : ''
    console.log(`${`${reading} C`.padEnd(10)}${change}${alert}`)
    was = on
    }

    // The dashboard heard every reading the node published.
    const heard: number[] = []
    for (let at = 0; at < 5; at += 1) {
    heard.push((await dashboard.recv())!.number!)
    }
    console.log(`heard ${heard.join(', ')} on ${profile.topic}`)

    // Between ticks the node waits as long as its battery allows: often on a healthy charge,
    // sparingly on a low one. run() does this until stopped, waiting each interval.
    for (const charge of [0.8, 0.3, 0.1]) {
    const { mode, waitMs } = node.schedule(charge)
    console.log(`battery at ${(charge * 100).toFixed(0)}% it runs ${mode} and waits ${waitMs / 1000} s`)
    }

    // The same file says how a dashboard draws the node.
    const element = profile.presentation!.elements[0]
    const [low, high] = element.band!
    console.log(`draws ${element.key} in ${element.unit} on a ${element.viz}, safe from ${low} to ${high}`)
    return lamp
    }
    Language Package Reference
    Rust pamoja-profile reference, docs.rs, install
    TypeScript @pamoja/profile reference, install
    Python pamoja-profile reference, install
    C# Pamoja.Profile reference, install

    MIT

    Controller
    Node
    PolicyRegistry
    Profile
    RuleEngine
    RuleEvaluator
    AlertReport
    ControlPolicy
    ElementSpec
    EngineRunOptions
    LinkMessage
    NodeOptions
    Policy
    PowerScheduleSettings
    PowerScheduleSpec
    Presentation
    Reaction
    Reads
    RuleAction
    RuleEngineOptions
    RuleFired
    RunOptions
    Theme
    Tick
    AlertKind
    ControlKind
    PolicyFactory
    RuleActionKind
    Viz
    AlertKind
    ControlKind
    RuleActionKind
    Viz