Level 4 - Create a Device Type Template Worker
Level 4 workers define device category templates that serve as abstract bases for brand-specific implementations.
A.1. Repository Naming Convention
Level 4 repositories follow strict naming patterns (see Naming Conventions).
miningos-tpl-wrk-{devicetype}Examples:
miningos-tpl-wrk-powermeter— Power monitoring equipmentminingos-tpl-wrk-miner— Mining hardwareminingos-tpl-wrk-sensor— Environmental sensorsminingos-tpl-wrk-container— Mining containers/racks
The tpl (template) designation distinguishes Level 4 workers from concrete implementations.
A.2. Directory Structure
A Level 4 template repository must follow this structure (see Repository Structure — Standard Directory Structure:
miningos-tpl-wrk-{devicetype}/
├── config/
│ ├── base.thing.json.example Device-specific configuration template
│ ├── common.json.example Common worker settings
│ └── facs/
│ ├── net.config.json.example Network/RPC configuration
│ └── store.config.json.example Storage configuration
├── mock/
│ └── mock-control-agent.js Mock device for testing
├── tests/
│ ├── cases/
│ │ └── {feature}.js Test case definitions
│ ├── schema/
│ │ └── {feature}.js Test schema validators
│ ├── {devicetype}.spec.js Main test file (*.spec.js pattern)
│ ├── executors.js Test executors
│ └── utils.js Test utilities
├── workers/
│ ├── lib/
│ │ ├── alerts.js Alert processing logic
│ │ ├── base.js Device base class (optional)
│ │ ├── constants.js Device-type constants
│ │ ├── stats.js Statistics processing
│ │ └── utils.js Utility functions
│ └── rack.{devicetype}.wrk.js Main template worker
├── LICENSE Apache-2.0
├── README.md Documentation
├── package.json Dependencies
├── setup-config.sh Configuration setup script
└── worker.js Entry pointFor testing structure details, see Testing & Linting Guidelines — Test Directory Structure.
A.3. Package.json Structure
{
"name": "miningos-tpl-wrk-{devicetype}",
"version": "0.0.1",
"description": "MiningOS Template Worker {DeviceType}",
"author": {
"name": "Your Name",
"email": "your.email@example.com"
},
"maintainers": [
{
"name": "Your Name",
"email": "your.email@example.com"
}
],
"keywords": ["miningos", "bitcoin", "mining"],
"scripts": {
"test": "brittle ./tests/**/*.spec.js",
"lint": "standard",
"lint:fix": "standard --fix"
},
"dependencies": {
"async": "3.2.6",
"bfx-svc-boot-js": "git+https://github.com/bitfinexcom/bfx-svc-boot-js.git",
"miningos-tpl-wrk-thing": "git+https://github.com/tetherto/miningos-tpl-wrk-thing.git"
},
"devDependencies": {
"brittle": "3.16.3",
"miningos-mock-control-service": "git+https://github.com/tetherto/miningos-mock-control-service.git"
},
"engine": {
"node": ">=16.0"
},
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/tetherto/miningos-tpl-wrk-{devicetype}.git"
}
}For linting and test script conventions, see Testing & Linting Guidelines.
A.4. Main Template Worker Implementation
The main worker file (workers/rack.\{devicetype\}.wrk.js) extends Level 3. For JSDoc annotation standards, see Code Documentation Standards:
'use strict'
const WrkRack = require('miningos-tpl-wrk-thing/workers/rack.thing.wrk')
class Wrk{DeviceType}Rack extends WrkRack {
/**
* Initialize the template worker.
* Configure device-category-specific scheduling and facilities.
*/
init () {
super.init()
// Configure additional statistics timeframes via code (optional)
// Example: Real-time data collection every 5 seconds
this.scheduleAddlStatTfs = [
['rtd', '*/5 * * * * *'] // Cron format: second granularity
]
// Note: Additional timeframes can also be configured via config file
// using `scheduleAddlStatConfigTfs` in base.thing.json
}
/**
* Returns the base type identifier for this device category.
* Level 5 implementations extend this with brand suffix.
* @returns {string} Device type identifier
*/
getThingType () {
return '{devicetype}' // e.g., 'powermeter', 'miner', 'sensor'
}
/**
* Returns the base type for statistics aggregation.
* Used by the stats system to group metrics.
* @returns {string} Base type identifier
*/
_getThingBaseType () {
return '{devicetype}'
}
/**
* Returns category-specific tags applied to all devices.
* @returns {string[]} Array of tag strings
*/
getSpecTags () {
return ['{devicetype}']
}
}
module.exports = Wrk{DeviceType}RackA.5. Abstract Methods Contract
Level 4 templates inherit abstract methods from Level 3 that must be implemented by Level 5 workers:
| Method | Purpose | Base Behavior | Level 5 Requirement |
|---|---|---|---|
async connectThing(thg) | Establish connection to physical device | No-op (empty stub) | Implement connection logic; return 0 on failure |
async collectThingSnap(thg) | Collect current device state snapshot | Throws ERR_IMPL_UNKNOWN | Must implement; return Object with stats and config |
selectThingInfo() | Select device info for API responses | Returns empty object \{\} | Override to return device-specific details |
async disconnectThing(thg) | Gracefully disconnect from device | Calls thg.ctrl.close() if available | Override only if custom cleanup needed |
async releaseIpThing(thg) | Release IP/network resources for device | No-op (empty stub) | Override if device requires IP resource cleanup |
connectThing is a no-op stub in Level 3. Level 5 implementations should return 0 from connectThing when connection fails to signal the failure to the snapshot collection loop.
Optional Lifecycle Hooks:
| Hook | When Called | Base Behavior |
|---|---|---|
async registerThingHook0(thg) | After new device registration | No-op |
async updateThingHook0(thg, thgPrev) | After device configuration update | Stores info changes to DB |
async forgetThingHook0(thg) | Before device removal | No-op |
async setupThingHook0(thg) | During device setup/loading | No-op |
async setupThingHook1(thg) | After loading last state from DB | No-op |
async collectSnapsHook0() | After snapshot collection cycle | No-op |
async tailLogHook0(logs, req) | During log tail operations | No-op |
Optional Extension Methods:
| Method | Purpose | Base Behavior |
|---|---|---|
_getWrkExtData(args) | Provide custom worker extension data for getWrkExtData RPC | Returns empty object \{\} |
A.6. Statistics Timeframe Configuration
Level 4 templates can configure additional statistics collection timeframes through two mechanisms:
Code-Defined Timeframes (scheduleAddlStatTfs)
Set in the init() method for timeframes that are integral to the device type:
init () {
super.init()
// Real-time data collection every 5 seconds
this.scheduleAddlStatTfs = [
['rtd', '*/5 * * * * *']
]
}Config-Defined Timeframes (scheduleAddlStatConfigTfs)
Set in base.thing.json for operator-customizable timeframes:
{
"thing": {
"scheduleAddlStatConfigTfs": [
["custom-hourly", "0 0 * * * *"]
]
}
}Both arrays are merged with the default timeframes from miningos-lib-stats during startup.
A.7. Configuration Templates
For complete configuration file documentation, see Repository Structure — Key Files Explained.
base.thing.json.example
{
"thing": {
"{devicetype}DefaultPort": 502,
"scheduleAddlStatConfigTfs": [],
"{devicetype}": {
"delay": 50,
"timeout": 30000,
"nominalValues": {
"exampleMetric": 100
}
}
}
}common.json.example
{
"debug": false,
"logging": true
}A.8. Testing Infrastructure
Level 4 templates must provide testing infrastructure that Level 5 implementations inherit. Test files must use the .spec.js extension to match the glob pattern in package.json. For complete testing guidelines, see Testing & Linting Guidelines.
tests/utils.js
'use strict'
const utils = require('miningos-tpl-wrk-thing/tests/utils')
const path = require('path')
// Extend parent test infrastructure
utils.SCHEMA_PATHS.push(path.join(__dirname, 'schema'))
utils.TEST_PATHS.push(path.join(__dirname, 'cases'))
module.exports = utilstests/{devicetype}.spec.js
'use strict'
const utils = require('./utils')
const executors = require('./executors')
// Test suite inherits from parent and adds device-specific tests
utils.runTests(executors)A.9. Worker Entry Point
worker.js
'use strict'
require('bfx-svc-boot-js')({
wtype: 'wrk-{devicetype}-rack',
conf: null
})A.10. Level 4 Checklist
- Repository created with correct naming (
miningos-tpl-wrk-\{devicetype\}) - Directory structure matches specification (see Repository Structure
-
package.jsonincludes all required dependencies -
package.jsonincludesmaintainersarray - Test files use
.spec.jsextension - Main worker extends
miningos-tpl-wrk-thing -
getThingType()returns device category identifier -
_getThingBaseType()returns base type for stats -
getSpecTags()returns category tags - Configuration examples provided (including
scheduleAddlStatConfigTfs) - Test infrastructure extends parent (see Testing & Linting Guidelines
- README.md includes all required sections (see Code Documentation Standards)
- Apache-2.0 license file included
-
setup-config.shcopies example configs
Level 4 - Create a Device Type Template Worker
Level 4 workers define device category templates that serve as abstract bases for brand-specific implementations.
A.1. Repository Naming Convention
Level 4 repositories follow strict naming patterns:
miningos-tpl-wrk-{devicetype}Examples:
miningos-tpl-wrk-powermeter— Power monitoring equipmentminingos-tpl-wrk-miner— Mining hardwareminingos-tpl-wrk-sensor— Environmental sensorsminingos-tpl-wrk-container— Mining containers or racks
The tpl (template) designation distinguishes Level 4 workers from concrete implementations.
A.2. Directory Structure
A Level 4 template repository must follow this structure:
miningos-tpl-wrk-{devicetype}/
├── config/
│ ├── base.thing.json.example Device-specific configuration template
│ ├── common.json.example Common worker settings
│ └── facs/
│ ├── net.config.json.example Network/RPC configuration
│ └── store.config.json.example Storage configuration
├── mock/
│ └── mock-control-agent.js Mock device for testing
├── tests/
│ ├── cases/
│ │ └── {feature}.js Test case definitions
│ ├── schema/
│ │ └── {feature}.js Test schema validators
│ ├── {devicetype}.spec.js Main test file (*.spec.js pattern)
│ ├── executors.js Test executors
│ └── utils.js Test utilities
├── workers/
│ ├── lib/
│ │ ├── alerts.js Alert processing logic
│ │ ├── base.js Device base class (optional)
│ │ ├── constants.js Device-type constants
│ │ ├── stats.js Statistics processing
│ │ └── utils.js Utility functions
│ └── rack.{devicetype}.wrk.js Main template worker
├── LICENSE Apache-2.0
├── README.md Documentation
├── package.json Dependencies
├── setup-config.sh Configuration setup script
└── worker.js Entry pointA.3. Package.json Structure
To create a package.json file for a Level 4 template:
-
Open a text editor and create the following JSON structure:
{ "name": "miningos-tpl-wrk-{devicetype}", "version": "0.0.1", "description": "MiningOS Template Worker {DeviceType}", "author": { "name": "Your Name", "email": "your.email@example.com" }, "maintainers": [ { "name": "Your Name", "email": "your.email@example.com" } ], "keywords": ["miningos", "bitcoin", "mining"], "scripts": { "test": "brittle ./tests/**/*.spec.js", "lint": "standard", "lint:fix": "standard --fix" }, "dependencies": { "async": "3.2.6", "bfx-svc-boot-js": "git+https://github.com/bitfinexcom/bfx-svc-boot-js.git", "miningos-tpl-wrk-thing": "git+https://github.com/tetherto/miningos-tpl-wrk-thing.git" }, "devDependencies": { "brittle": "3.16.3", "miningos-mock-control-service": "git+https://github.com/tetherto/miningos-mock-control-service.git" }, "engine": { "node": ">=16.0" }, "license": "Apache-2.0", "repository": { "type": "git", "url": "git+https://github.com/tetherto/miningos-tpl-wrk-{devicetype}.git" } } -
Replace
\{devicetype\}with your specific device category (e.g.,powermeter). -
Update the
authorandmaintainersfields with your information. -
Save the file as
package.jsonin your repository root.
A.4. Main Template Worker Implementation
The main worker file (workers/rack.\{devicetype\}.wrk.js) extends Level 3:
To implement the main worker file:
-
Create the file at
workers/rack.\{devicetype\}.wrk.js. -
Add the following structure, replacing
\{DeviceType\}with your device category (e.g.,PowerMeter):'use strict' const WrkRack = require('miningos-tpl-wrk-thing/workers/rack.thing.wrk') class Wrk{DeviceType}Rack extends WrkRack { /** * Initialize the template worker. * Configure device-category-specific scheduling and facilities. */ init () { super.init() // Configure additional statistics timeframes via code (optional) // Example: Real-time data collection every 5 seconds this.scheduleAddlStatTfs = [ ['rtd', '*/5 * * * * *'] // Cron format: second granularity ] // Note: Additional timeframes can also be configured via config file // using `scheduleAddlStatConfigTfs` in base.thing.json } /** * Returns the base type identifier for this device category. * Level 5 implementations extend this with brand suffix. * @returns {string} Device type identifier */ getThingType () { return '{devicetype}' // e.g., 'powermeter', 'miner', 'sensor' } /** * Returns the base type for statistics aggregation. * Used by the stats system to group metrics. * @returns {string} Base type identifier */ _getThingBaseType () { return '{devicetype}' } /** * Returns category-specific tags applied to all devices. * @returns {string[]} Array of tag strings */ getSpecTags () { return ['{devicetype}'] } } module.exports = Wrk{DeviceType}Rack
A.5. Abstract Methods Contract
Level 4 templates inherit abstract methods from Level 3 that must be implemented by Level 5 workers:
| Method | Purpose | Base Behavior | Level 5 Requirement |
|---|---|---|---|
async connectThing(thg) | Establish connection to physical device | No-op (empty stub) | Implement connection logic; return 0 on failure |
async collectThingSnap(thg) | Collect current device state snapshot | Throws ERR_IMPL_UNKNOWN | Must implement; return Object with stats and config |
selectThingInfo() | Select device info for API responses | Returns empty object \{\} | Override to return device-specific details |
async disconnectThing(thg) | Gracefully disconnect from device | Calls thg.ctrl.close() if available | Override only if custom cleanup needed |
async releaseIpThing(thg) | Release IP/network resources for device | No-op (empty stub) | Override if device requires IP resource cleanup |
connectThing is a no-op stub in Level 3. Level 5 implementations should return 0 from connectThing when connection fails to signal the failure to the snapshot collection loop.
Optional Lifecycle Hooks:
| Hook | When Called | Base Behavior |
|---|---|---|
async registerThingHook0(thg) | After new device registration | No-op |
async updateThingHook0(thg, thgPrev) | After device configuration update | Stores info changes to DB |
async forgetThingHook0(thg) | Before device removal | No-op |
async setupThingHook0(thg) | During device setup or loading | No-op |
async setupThingHook1(thg) | After loading last state from DB | No-op |
async collectSnapsHook0() | After snapshot collection cycle | No-op |
async tailLogHook0(logs, req) | During log tail operations | No-op |
Optional Extension Methods:
| Method | Purpose | Base Behavior |
|---|---|---|
_getWrkExtData(args) | Provide custom worker extension data for getWrkExtData RPC | Returns empty object \{\} |
A.6. Statistics Timeframe Configuration
Level 4 templates can configure additional statistics collection timeframes through two mechanisms:
Code-Defined Timeframes (scheduleAddlStatTfs)
Set in the init() method for timeframes that are integral to the device type:
-
In your main worker file, modify the
init()method:init () { super.init() // Real-time data collection every 5 seconds this.scheduleAddlStatTfs = [ ['rtd', '*/5 * * * * *'] ] }
Config-Defined Timeframes (scheduleAddlStatConfigTfs)
Set in base.thing.json for operator-customizable timeframes:
-
Edit your
config/base.thing.json.examplefile:{ "thing": { "scheduleAddlStatConfigTfs": [ ["custom-hourly", "0 0 * * * *"] ] } }
Both arrays are merged with the default timeframes from miningos-lib-stats during startup.
A.7. Configuration Templates
base.thing.json.example
-
Create or edit the
config/base.thing.json.examplefile:{ "thing": { "{devicetype}DefaultPort": 502, "scheduleAddlStatConfigTfs": [], "{devicetype}": { "delay": 50, "timeout": 30000, "nominalValues": { "exampleMetric": 100 } } } } -
Replace
\{devicetype\}with your device category.
common.json.example
-
Create the
config/common.json.examplefile:{ "debug": false, "logging": true }
A.8. Testing Infrastructure
Level 4 templates must provide testing infrastructure that Level 5 implementations inherit. Test files must use the .spec.js extension to match the glob pattern in package.json.
tests/utils.js
To extend the parent test infrastructure:
-
Create
tests/utils.js:'use strict' const utils = require('miningos-tpl-wrk-thing/tests/utils') const path = require('path') // Extend parent test infrastructure utils.SCHEMA_PATHS.push(path.join(__dirname, 'schema')) utils.TEST_PATHS.push(path.join(__dirname, 'cases')) module.exports = utils
tests/{devicetype}.spec.js
To create the main test file create a tests/\{devicetype\}.spec.js file:
'use strict'
const utils = require('./utils')
const executors = require('./executors')
// Test suite inherits from parent and adds device-specific tests
utils.runTests(executors)A.9. Worker Entry Point
worker.js
-
Create the
worker.jsentry point file:'use strict' require('bfx-svc-boot-js')({ wtype: 'wrk-{devicetype}-rack', conf: null })
A.10. Level 4 Checklist
- Repository created with correct naming (
miningos-tpl-wrk-\{devicetype\}) - Directory structure matches specification
-
package.jsonincludes all required dependencies -
package.jsonincludesmaintainersarray - Test files use
.spec.jsextension - Main worker extends
miningos-tpl-wrk-thing -
getThingType()returns device category identifier -
_getThingBaseType()returns base type for stats -
getSpecTags()returns category tags - Configuration examples provided (including
scheduleAddlStatConfigTfs) - Test infrastructure extends parent
- README.md includes all required sections
- Apache-2.0 license file included
-
setup-config.shcopies example configs