Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/hobbyquaker/mqtt-scripts

Node.js based script runner for use in MQTT based Smart Home environments
https://github.com/hobbyquaker/mqtt-scripts

automation iot mqtt nodejs rules-engine script-engine script-runner scripting smarthome

Last synced: about 2 months ago
JSON representation

Node.js based script runner for use in MQTT based Smart Home environments

Awesome Lists containing this project

README

        

# mqtt-scripts

[![mqtt-smarthome](https://img.shields.io/badge/mqtt-smarthome-blue.svg)](https://github.com/mqtt-smarthome/mqtt-smarthome)
[![NPM version](https://badge.fury.io/js/mqtt-scripts.svg)](http://badge.fury.io/js/mqtt-scripts)
[![dependencies Status](https://david-dm.org/hobbyquaker/mqtt-scripts/status.svg)](https://david-dm.org/hobbyquaker/mqtt-scripts)
[![Build Status](https://travis-ci.org/hobbyquaker/mqtt-scripts.svg?branch=master)](https://travis-ci.org/hobbyquaker/mqtt-scripts)
[![Coverage Status](https://coveralls.io/repos/github/hobbyquaker/mqtt-scripts/badge.svg?branch=master)](https://coveralls.io/github/hobbyquaker/mqtt-scripts?branch=master)
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![License][mit-badge]][mit-url]

> mqtt-scripts is a Node.js based script runner for use in mqtt based smart home environments.

It's intentended to be used as the "logic layer" in your smart home, and offers a zero-boilerplate, straight forward
scripting environment.

It follows the [mqtt-smarthome](https://github.com/mqtt-smarthome/mqtt-smarthome) architecture. Mqtt-scripts could be
seen as something like "Node-RED without GUI"

# Getting started

Prerequisites: mqtt-scripts needs Node.js >= 6.0.

* Install mqtt-scripts globally:

```sudo npm install -g mqtt-scripts```

* Create a folder from where mqtt-scripts will load the scripts:

```mkdir -p /opt/mqtt-smarthome/scripts```

* Create a folder to install node modules that can be used in the scripts:

```mkdir /opt/mqtt-smarthome/scripts/node_modules```
(You can then just use npm install in the directory /opt/mqtt-smarthome/scripts)

* Put some files in you script dir:

```
echo "log.info('my first script!')" > /opt/mqtt-smarthome/scripts/test1.js
echo "log.info 'get ma a coffee' > /opt/mqtt-smarthome/scripts/test1.coffee
```

* Start mqtt-scripts

```mqtt-scripts -d /opt/mqtt-smarthome/scripts```

### Run with Docker

To run with Docker, use either a [pre-build image](https://hub.docker.com/r/dersimn/mqtt-scripts) or build one your own. Either way, just substitude the node-command you would have used by the Docker command, for e.g.:

```mqtt-scripts --help```

becomes

```docker run dersimn/mqtt-scripts:1 --help```

An example for a productive configuration would be:

```
docker run -d --restart=always --name=logic \
-e "TZ=Europe/Berlin" \
-v /opt/hma/etc/scripts:/scripts:ro \
dersimn/mqtt-scripts:1 \
--url mqtt://10.1.1.50 \
--dir /scripts
```

Configure via `MQTTSCRIPTS_` env variables when using Docker Compose.

#### Build

Docker development build:

docker build -t mqtt-scripts .
docker run --rm mqtt-scripts --help

Docker Hub deploy:

docker buildx create --name mybuilder
docker buildx use mybuilder
docker buildx build --platform linux/amd64,linux/arm/v7 -t dersimn/mqtt-scripts:1 -t dersimn/mqtt-scripts:1.x.x --push .

# Command Line Options


Usage: mqtt-scripts [options]

Options:
--version Show version number [boolean]
-c, --config Path to JSON config file
-d, --dir directory to scan for .js and .coffee files. can be
used multiple times.
-h, --help Show help [boolean]
-s, --variable-prefix topic prefix for $ substitution (shorthand for
variables, see docs) [default: "var"]
-t, --disable-variables disable variable feedback (see docs) [default: false]
-n, --name instance name. used as mqtt client id and as prefix
for connected topic [default: "logic"]
-u, --url mqtt broker url. See
https://github.com/mqttjs/MQTT.js#connect-using-a-url
[default: "mqtt://127.0.0.1"]
-v, --verbosity possible values: "error", "warn", "info", "debug"
[default: "info"]
-w, --disable-watch disable file watching (don't exit process on file
changes) [default: false]
-l, --latitude [default: 48.7408]
-m, --longitude [default: 9.1778]

If you're running multiple instances of mqtt-scripts you have to decide which one should handle variables and disable
the variables on all other instances with the --disable-variable option.

# Script Examples

#### Use hm2mqtt and hue2mqtt to control a hue lamp with a homematic remote control

```javascript
link('hm//RC4:1/PRESS_CONT', 'hue//lights/Hobbyraum/bri_inc', -16);

subscribe('hm//RC4:2/PRESS_CONT', function () {
if (!getValue('hue//lights/Hobbyraum')) {
setValue('hue//lights/Hobbyraum', 1);
} else {
setValue('hue//lights/Hobbyraum/bri_inc', 16);
}
});

link('hm//RC4:1/PRESS_SHORT', 'hue//lights/Hobbyraum', 0);
link('hm//RC4:2/PRESS_SHORT', 'hue//lights/Hobbyraum', 254);
link('hm//RC4:3/PRESS_CONT', 'hue//lights/Hobbyraum/ct_inc', -16);
link('hm//RC4:4/PRESS_CONT', 'hue//lights/Hobbyraum/ct_inc', 16);
link('hm//RC4:3/PRESS_SHORT', 'hue//lights/Hobbyraum/ct', 153);
link('hm//RC4:4/PRESS_SHORT', 'hue//lights/Hobbyraum/ct', 500);
```

#### retrieve fuel prices from tankerkoenig

```javascript
var request = require('request');
var cred = require('./lib/credentials.js');

var url = 'https://creativecommons.tankerkoenig.de/json/detail.php';

var tankstellen = {
'OMV': 'cb1f0588-d517-40f0-8ce3-3edadebea40d',
'Shell': '4267c196-eea1-47be-96b7-d790b2fbd17a'
};

schedule('0/12 * * * *', function () {
for (var topic in tankstellen) {
getData(topic, tankstellen[topic]);
}
});

function getData(topic, id) {
request.get(url + '?id=' + id + '&apikey=' + cred.tankerkoenig.apikey, function (err, res) {
if (err) {
log.error(err);
return;
}
var data = JSON.parse(res.body).station;
setValue('$Tankstelle/' + topic + '/Diesel', data.diesel);
setValue('$Tankstelle/' + topic + '/E5', data.e5);
setValue('$Tankstelle/' + topic + '/Offen', data.isOpen);
});
}
```

#### Send a variables state changes to Pushover

```Javascript
var cred = require('./lib/credentials.js');

var pushoverNotifications = require('pushover-notifications');

var push = new pushoverNotifications( {
user: cred.pushover.user,
token: cred.pushover.token,
onerror: function (error) {
log.error(error);
}
});

function pushover(msg) {
if (typeof msg !== 'object' || typeof msg.message !== 'string') msg = {message: '' + msg};
msg.title = msg.title || "Smart Home";
msg.priority = msg.priority || 0;
msg.device = msg.device || 'iphone5';
push.send(msg, function(err, result) {
if (err) {
log.error(err);
}
});
}

subscribe('$Anwesenheit', {change: true}, function () {
pushover({
title:'Anwesenheit',
message: getProp($Anwesenheit, 'logic_textual'),
priority: -1
});
});
```

# API

## Classes


log


Log to stdout/stderr. Messages are prefixed with a timestamp and the calling scripts path.



## Functions


subscribe(topic, [options], callback)


Subscribe to MQTT topic(s)



schedule(pattern, [options], callback)


Schedule recurring and one-shot events



sunSchedule(pattern, [options], callback)


Schedule a recurring event based on sun position



publish(topic, payload, [options])


Publish a MQTT message



setValue(topic, val)


Set a value on one or more topics




getValue(topic)mixed



getProp(topic, [...property])mixed


Get a specific property of a topic




now()number



age(topic)number


link(source, target, [value])


Link topic(s) to other topic(s)



combineBool(srcs, targets)


Combine topics through boolean or



combineMax(srcs, targets)


Publish maximum of combined topics



timer(src, target, time)


Publishes 1 on target for specific time after src changed to true



## Typedefs



subscribeCallback : function


## log
Log to stdout/stderr. Messages are prefixed with a timestamp and the calling scripts path.

**Kind**: global class

* [log](#log)
* [.debug()](#log.debug)
* [.info()](#log.info)
* [.warn()](#log.warn)
* [.error()](#log.error)

### log.debug()
Log a debug message

**Kind**: static method of [log](#log)

| Type |
| --- |
| \* |

### log.info()
Log an info message

**Kind**: static method of [log](#log)

| Type |
| --- |
| \* |

### log.warn()
Log a warning message

**Kind**: static method of [log](#log)

| Type |
| --- |
| \* |

### log.error()
Log an error message

**Kind**: static method of [log](#log)

| Type |
| --- |
| \* |

## subscribe(topic, [options], callback)
Subscribe to MQTT topic(s)

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| topic | string \| Array.<string> | topic or array of topics to subscribe |
| [options] | Object \| string \| function | Options object or as shorthand to options.condition a function or string |
| [options.shift] | number | delay execution in seconds. Has to be positive |
| [options.random] | number | random delay execution in seconds. Has to be positive |
| [options.change] | boolean | if set to true callback is only called if val changed |
| [options.retain] | boolean | if set to true callback is also called on retained messages |
| [options.condition] | string \| function | conditional function or condition string |
| callback | [subscribeCallback](#subscribeCallback) | |

## schedule(pattern, [options], callback)
Schedule recurring and one-shot events

**Kind**: global function
**See**: [sunSchedule](#sunSchedule) for scheduling based on sun position.

| Param | Type | Description |
| --- | --- | --- |
| pattern | string \| Date \| Object \| Array.<mixed> | pattern or array of patterns. May be cron style string, Date object or node-schedule object literal. See [https://github.com/tejasmanohar/node-schedule/wiki](https://github.com/tejasmanohar/node-schedule/wiki) |
| [options] | Object | |
| [options.random] | number | random delay execution in seconds. Has to be positive |
| callback | function | is called with no arguments |

**Example**
```js
// every full Hour.
schedule('0 * * * *', callback);

// Monday till friday, random between 7:30am an 8:00am
schedule('30 7 * * 1-5', {random: 30 * 60}, callback);

// once on 21. December 2018 at 5:30am
schedule(new Date(2018, 12, 21, 5, 30, 0), callback);

// every Sunday at 2:30pm
schedule({hour: 14, minute: 30, dayOfWeek: 0}, callback);
```

## sunSchedule(pattern, [options], callback)
Schedule a recurring event based on sun position

**Kind**: global function
**See**: [schedule](#schedule) for time based scheduling.

| Param | Type | Description |
| --- | --- | --- |
| pattern | string \| Array.<string> | a suncalc event or an array of suncalc events. See [https://github.com/mourner/suncalc](https://github.com/mourner/suncalc) |
| [options] | Object | |
| [options.shift] | number | delay execution in seconds. Allowed Range: -86400...86400 (+/- 24h) |
| [options.random] | number | random delay execution in seconds. |
| callback | function | is called with no arguments |

**Example**
```js
// Call callback 15 minutes before sunrise
sunSchedule('sunrise', {shift: -900}, callback);

// Call callback random 0-15 minutes after sunset
sunSchedule('sunset', {random: 900}, callback);
```

## publish(topic, payload, [options])
Publish a MQTT message

**Kind**: global function

| Param | Type | Default | Description |
| --- | --- | --- | --- |
| topic | string \| Array.<string> | | topic or array of topics to publish to |
| payload | string \| Object | | the payload string. If an object is given it will be JSON.stringified |
| [options] | Object | | the options to publish with |
| [options.qos] | number | 0 | QoS Level |
| [options.retain] | boolean | false | retain flag |

## setValue(topic, val)
Set a value on one or more topics

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| topic | string \| Array.<string> | topic or array of topics to set value on |
| val | mixed | |

## getValue(topic) ⇒ mixed
**Kind**: global function
**Returns**: mixed - the topics value

| Param | Type |
| --- | --- |
| topic | string |

## getProp(topic, [...property]) ⇒ mixed
Get a specific property of a topic

**Kind**: global function
**Returns**: mixed - the topics properties value

| Param | Type | Description |
| --- | --- | --- |
| topic | string | |
| [...property] | string | the property to retrieve. May be repeated for nested properties. If omitted the whole topic object is returned. |

**Example**
```js
// returns the timestamp of a given topic
getProp('hm//Bewegungsmelder Keller/MOTION', 'ts');
```

## now() ⇒ number
**Kind**: global function
**Returns**: number - ms since epoch

## age(topic) ⇒ number
**Kind**: global function
**Returns**: number - seconds since last change

| Param | Type |
| --- | --- |
| topic | string |

## link(source, target, [value])
Link topic(s) to other topic(s)

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| source | string \| Array.<string> | topic or array of topics to subscribe |
| target | string \| Array.<string> | topic or array of topics to publish |
| [value] | mixed | value to publish. If omitted the sources value is published. A function can be used to transform the value. |

## combineBool(srcs, targets)
Combine topics through boolean or

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| srcs | Array.<string> | array of topics to subscribe |
| targets | string | topic to publish |

## combineMax(srcs, targets)
Publish maximum of combined topics

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| srcs | Array.<string> | array of topics to subscribe |
| targets | string | topic to publish |

## timer(src, target, time)
Publishes 1 on target for specific time after src changed to true

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| src | string \| Array.<string> | topic or array of topics to subscribe |
| target | string | topic to publish |
| time | number | timeout in milliseconds |

## subscribeCallback : function
**Kind**: global typedef

| Param | Type | Description |
| --- | --- | --- |
| topic | string | the topic that triggered this callback. +/status/# will be replaced by +//# |
| val | mixed | the val property of the new state |
| obj | object | new state - the whole state object (e.g. {"val": true, "ts": 12346345, "lc": 12346345} ) |
| objPrev | object | previous state - the whole state object |
| msg | object | the mqtt message as received from MQTT.js |

# License

MIT © [Sebastian Raff](https://github.com/hobbyquaker)

[mit-badge]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat
[mit-url]: LICENSE