npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@basmilius/homey-common

v1.24.0

Published

Common code shared across my Homey apps.

Readme


@basmilius/homey-common

Shared TypeScript base classes, decorators, and utilities for Homey apps. Provides a typed foundation for apps, devices, drivers, and flow cards so every app starts from a consistent, well-typed base.

Installation

bun add @basmilius/homey-common

Prerequisites

  • Bun >= 1.3.0
  • Node >= 25

Overview

| Export | Description | |-----------------------------------------------------|------------------------------------------------------------------------------| | App | Base class for Homey apps with a built-in flow card registry | | Device | Base class for Homey devices with typed getters and prefixed logging | | Driver | Base class for Homey drivers | | Registry | Central registry for registering and looking up flow card entities | | FlowActionEntity | Base class for flow action cards | | FlowConditionEntity | Base class for flow condition cards | | FlowTriggerEntity | Base class for global flow trigger cards | | FlowDeviceTriggerEntity | Base class for device-specific flow trigger cards | | FlowAutocompleteProvider | Base class for flow argument autocomplete providers | | FlowAutocompleteArgumentProvider | Autocomplete provider that collects values from existing flow card arguments | | action / condition / trigger / autocomplete | Decorators for assigning flow card IDs | | DateTime / Luxon | Re-exported from luxon |

@basmilius/homey-common/data

| Export | Description | |---------------------|----------------------------------------------------| | colors / icons | Predefined color palette and Font Awesome icon set |


Usage

App

Extend App to get a typed registry for your flow cards:

import { App } from '@basmilius/homey-common';

export class MyApp extends App<MyApp> {

    async onInit(): Promise<void> {
        this.registry.action(MyAction);
        this.registry.condition(MyCondition);
        this.registry.trigger(MyTrigger);
        this.registry.autocompleteProvider(MyAutocomplete);
    }

}

Device & Driver

import { Device, Driver } from '@basmilius/homey-common';
import type { MyApp } from './app';

export class MyDriver extends Driver<MyApp> {

    async onInit(): Promise<void> {
        // this.app → typed MyApp instance
    }

}

export class MyDevice extends Device<MyApp, MyDriver> {

    async onInit(): Promise<void> {
        // this.app       → typed MyApp instance
        // this.appDriver → typed MyDriver instance
        // this.id        → device ID
        // this.name      → device name

        await this.syncCapabilities(['measure_temperature', 'onoff']);
    }

}

Flow cards

Use the @action, @condition, @trigger decorators to bind a class to a flow card ID, then implement onRun:

import { FlowActionEntity, action } from '@basmilius/homey-common';
import type { MyApp } from './app';

type Args = { message: string };

@action('send_notification')
export class SendNotificationAction extends FlowActionEntity<MyApp, Args> {

    async onRun(args: Args): Promise<void> {
        await this.notify(args.message);
    }

}
import { FlowConditionEntity, condition } from '@basmilius/homey-common';
import type { MyApp } from './app';

type Args = { threshold: number };

@condition('temperature_above')
export class TemperatureAboveCondition extends FlowConditionEntity<MyApp, Args> {

    async onRun(args: Args): Promise<boolean> {
        const temp = this.app.getCurrentTemperature();
        return temp > args.threshold;
    }

}
import { FlowTriggerEntity, trigger } from '@basmilius/homey-common';
import type { MyApp } from './app';

type Tokens = { temperature: number };

@trigger('temperature_changed')
export class TemperatureChangedTrigger extends FlowTriggerEntity<MyApp, unknown, unknown, Tokens> {

    async onRun(): Promise<boolean> {
        return true;
    }

}

// Fire the trigger from elsewhere in your app:
const t = app.registry.findTrigger(TemperatureChangedTrigger)!;
await t.trigger({}, { temperature: 21.5 });

Device triggers

import { FlowDeviceTriggerEntity, trigger } from '@basmilius/homey-common';
import type { MyApp } from './app';
import type { MyDevice } from './device';

@trigger('button_pressed')
export class ButtonPressedTrigger extends FlowDeviceTriggerEntity<MyApp, MyDevice> {

    async onRun(): Promise<boolean> {
        return true;
    }

}

// Fire from the device:
const t = device.app.registry.findDeviceTrigger(ButtonPressedTrigger)!;
await t.trigger(device, {});

Autocomplete

import { FlowAutocompleteProvider, autocomplete } from '@basmilius/homey-common';
import type { MyApp } from './app';

@autocomplete('scene')
export class SceneAutocomplete extends FlowAutocompleteProvider<MyApp> {

    async find(query: string): Promise<Homey.FlowCard.ArgumentAutocompleteResults> {
        const scenes = await this.app.getScenes();

        return scenes
            .filter(scene => scene.name.toLowerCase().includes(query.toLowerCase()))
            .map(scene => ({ name: scene.name, id: scene.id }));
    }

}

Register the autocomplete provider before the action that uses it, then bind them:

this.registry.autocompleteProvider(SceneAutocomplete);
this.registry.action(ActivateSceneAction);

// Inside ActivateSceneAction.onInit:
this.registerAutocomplete('scene', SceneAutocomplete);

Registry

The registry is created automatically by App. Use it to register and look up entities:

// Register
registry.action(MyAction);
registry.condition(MyCondition);
registry.trigger(MyTrigger);
registry.deviceTrigger(MyDeviceTrigger);
registry.autocompleteProvider(MyAutocomplete);

// Register a simple action without a class
registry.actionFunction<{ value: number }>('set_value', (args) => {
    console.log(args.value);
});

// Look up a registered entity
const trigger = registry.findTrigger(MyTrigger);

Helpers from Shortcuts

App, FlowEntity, FlowAutocompleteProvider, and Shortcuts all expose the following helpers:

this.app           // Typed app instance
this.homey         // Homey instance
this.registry      // Flow card registry
this.flow          // homey.flow
this.settings      // homey.settings
this.discovery     // homey.discovery
this.dashboards    // homey.dashboards
this.language      // Current language code

await this.notify('Hello from the timeline!');
this.translate('my.key', { name: 'World' });
this.realtime('my_event', { data: 123 });

const interval = this.setInterval(() => { ... }, 5000);
this.clearInterval(interval);

Data

import { colors, icons } from '@basmilius/homey-common/data';

// colors: { hex: string; label: string }[]
// icons:  { id: string; name: string; unicode: string }[]

Types

import type {
    ApiRequest,        // Typed Homey API request
    WidgetApiRequest,  // Typed Homey widget API request
    Language,          // Supported language codes
    Color,             // Color entry
    Icon,              // Icon entry
    FlowCard,          // Union of all Homey flow card types
    FlowCardType,      // 'action' | 'condition' | 'trigger'
} from '@basmilius/homey-common';

// API handler example
async function onRequest(request: ApiRequest<MyApp, { value: number }>) {
    const app = request.homey.app;
    const value = request.body.value;
}

Development

bun install   # Install dependencies
bun run build # Build to dist/

License

MIT — © Bas Milius