├── .eslintrc.js
├── .github
└── workflows
│ ├── node.js-ci.yml
│ └── npm.yml
├── .gitignore
├── .npmignore
├── .vscode
└── tasks.json
├── CONTRIBUTING.md
├── LICENSE
├── README.MD
├── _template.html
├── _template.js
├── createNode.ps1
├── dashboard
└── zigbee2mqtt_dashboard_node_red.json
├── docker-compose.yml
├── docs
├── config
│ ├── bridge-config.md
│ ├── device-config.md
│ ├── img
│ │ ├── bridge-config-config.png
│ │ ├── device-config-config-nonz2m-support.png
│ │ ├── device-config-config.png
│ │ └── mqtt-config-config.png
│ └── mqtt-config.md
├── docker-compose.yml
├── documentation.md
├── getting-started.md
├── img
│ ├── 0-18-update-delete-old-nodes.png
│ ├── 0-18-update-reconfigue-bridge.png
│ ├── 0.18-update-new-mqttt-config.png
│ ├── getting-started-flow01-send-messages.png
│ ├── getting-started-flow02-bridge-config.png
│ ├── getting-started-flow03-mqtt-config.png
│ ├── getting-started-flow04-connected.png
│ ├── getting-started-flow05-generic-lamp.png
│ ├── getting-started-flow06-hue-device-config.png
│ ├── getting-started-flow07-brightness-color-hue.png
│ ├── getting-started-flow08-inject-node.png
│ ├── getting-started-flow09-toggle-device-config.png
│ ├── getting-started-flow10-toggle.gif
│ ├── getting-started-flow11-off-device-config.png
│ ├── getting-started-flow12-on-off-flow-1.png
│ ├── getting-started-flow12-on-off-flow-2.png
│ ├── getting-started-flow13-on-off-flow-3.gif
│ ├── getting-started-flow14-second-lamp-paralell.png
│ ├── getting-started-flow15-second-lamp-series.png
│ ├── getting-started-flow16-override-nodes.gif
│ ├── getting-started-flow17-override-multiple.gif
│ ├── getting-started-flow18-inject-override.png
│ ├── getting-started-flow19-inject-override-flow.png
│ ├── getting-started-install-palette.png
│ ├── getting-started-z2m-renamed.png
│ └── overview.png
└── nodes
│ ├── bridge-log.md
│ ├── button-switch.md
│ ├── climate-sensor.md
│ ├── device-status.md
│ ├── generic-lamp.md
│ ├── get-lamp-state.md
│ ├── img
│ ├── bridge-log-config-consolidate-output.png
│ ├── bridge-log-config.png
│ ├── bridge-log-flow-consolidate-output.png
│ ├── bridge-log-flow.png
│ ├── button-switch-config-hold-repeat.png
│ ├── button-switch-config.png
│ ├── button-switch-flow.png
│ ├── climate-sensor-config.png
│ ├── device-status-config-experimental.png
│ ├── device-status-config.png
│ ├── generic-lamp-config.png
│ ├── generic-lamp-flow.png
│ ├── get-lamp-state-config.png
│ ├── ota-node-autoUpdate-msg.png
│ ├── ota-node-config-blacklist.png
│ ├── ota-node-config.png
│ ├── ota-node-context-updates-available.png
│ ├── ota-node-example-flow-over-night.png
│ ├── ota-node-update-available.png
│ ├── ota-node-update-finished.png
│ ├── ota-node-update-in-progress.png
│ ├── ota-node-update-progress-output.png
│ ├── override-action-config.png
│ ├── override-brightness-config.png
│ ├── override-color-config.png
│ ├── override-nodes-example.png
│ ├── override-state-config.png
│ ├── override-temperature-config.png
│ ├── scene-in-config.png
│ ├── scene-selector-config.png
│ ├── scene-selector-example.png
│ ├── send-messages-config.png
│ ├── shelly25-config.png
│ ├── shelly25-listen-input.png
│ ├── shelly25-toggle-relay-settings.png
│ └── shelly25-toggle-relay.png
│ ├── ota-node.md
│ ├── override-action.md
│ ├── override-brightness.md
│ ├── override-color.md
│ ├── override-nodes.md
│ ├── override-state.md
│ ├── override-temperature.md
│ ├── scene-in.md
│ ├── scene-selector.md
│ ├── send-messages.md
│ └── shelly-25-node.md
├── examples
├── getting-started
│ ├── one-lamp-on-off.json
│ └── two-lamps-override.json
├── ota
│ └── ota_over_night.json
├── scenes
│ └── basic_scene.json
└── shelly-25
│ ├── shelly25-listen-to-input.json
│ └── shelly25-toggle-relay.json
├── install.ps1
├── package-lock.json
├── package.json
├── resources
├── logo.svg
└── logo_white_bg.svg
├── robocopy.ps1
├── src
├── _api.html
├── _api.js
├── bridge-log.html
├── bridge-log.js
├── device-types.ts
├── icons
│ ├── remote-black.svg
│ └── remote.svg
├── lib
│ ├── mqtt.ts
│ ├── outputHandler.js
│ └── utils.js
├── nodes
│ ├── devices-eurotronic.html
│ ├── devices-eurotronic.js
│ ├── devices-ikea.html
│ ├── devices-ikea.js
│ ├── devices-scenic.html
│ ├── devices-scenic.js
│ ├── devices-sonoff.html
│ ├── devices-sonoff.js
│ ├── devices-tasmota.html
│ ├── devices-tasmota.js
│ ├── devices-tint.html
│ ├── devices-tint.js
│ ├── hue-dimmer
│ │ ├── devices-hue.html
│ │ └── devices-hue.js
│ ├── override
│ │ ├── override-nodes.html
│ │ ├── override-nodes.ts
│ │ └── types.ts
│ ├── scenes.html
│ ├── scenes.js
│ ├── sensors.html
│ └── sensors.js
├── non-z2m-nodes
│ ├── devices-shelly-25.html
│ └── devices-shelly-25.js
├── ota.html
├── ota.js
├── types.ts
├── zigbee2mqtt-config.html
├── zigbee2mqtt-config.ts
├── zigbee2mqtt.html
└── zigbee2mqtt.js
├── test-integration
├── docker
│ ├── .gitignore
│ ├── docker-compose.yml
│ └── mosquitto
│ │ └── config
│ │ └── mosquitto.conf
└── non-z2m-nodes
│ └── devices-shelly-25_spec.js
├── test
├── scenes_scene-selector_spec.js
└── zigbee2mqtt_generic-lamp_spec.js
├── tsconfig.json
└── upcoming-changelog.md
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "node": true,
6 | "amd": true,
7 | "mocha": true
8 | },
9 | "extends": "eslint:recommended",
10 | "parserOptions": {
11 | "ecmaVersion": 12,
12 | "sourceType": "module"
13 | },
14 | "rules": {
15 | "indent": [
16 | "error",
17 | 4,
18 | {
19 | "SwitchCase":1
20 | }
21 |
22 | ],
23 | "linebreak-style": [
24 | "off",
25 | "unix"
26 | ],
27 | "quotes": [
28 | "warn",
29 | "double"
30 | ],
31 | "semi": [
32 | "error",
33 | "always"
34 | ],
35 | "no-unused-vars" : [
36 | "off"
37 | ]
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/.github/workflows/node.js-ci.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [12.x]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v1
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | - run: npm ci
28 | - run: npm run build
29 | - run: npm run test
30 | - run: npm run testintegration
31 | - run: npm run lint
32 | - run: npm install -g node-red-dev
33 | - run: node-red-dev validate
34 |
35 |
--------------------------------------------------------------------------------
/.github/workflows/npm.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Release Node.js Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | publish-npm:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - uses: actions/setup-node@v1
16 | with:
17 | node-version: 12
18 | registry-url: https://registry.npmjs.org/
19 | - run: npm ci
20 | - run: npm run build
21 | - run: npm publish
22 | env:
23 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .idea
3 | debug
4 | node_modules
5 | dist
6 | node-red-contrib-zigbee2mqtt-devices*.tgz
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*/
2 | src/
3 | test/
4 | test-integration/
5 | debug
6 | *.ps1
7 | docker-compose.yml
8 | tsconfig.json
9 | _template.html
10 | _template.js
11 | upcoming-changelog.md
12 | docs/
13 | .eslintrc.js
14 | dashboard/
15 | resources/
16 | node-red-contrib-zigbee2mqtt-devices*.tgz
17 | CONTRIBUTING.md
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "node-red deploy",
6 | "command": "npm run builddocker",
7 | "type": "shell",
8 | "problemMatcher": [],
9 | "group": {
10 | "kind": "build",
11 | "isDefault": true
12 | }
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Everyone is welcome to create a pull request. If you are unsure what or how to do something, we can discuss it on [GitHub](https://github.com/Dirnei/node-red-contrib-zigbee2mqtt-devices) or [Discord](https://discord.gg/4qCMEhJ).
4 |
5 |
6 | # Development Environment
7 |
8 | The project is written in JavaScript, HTML, and Typescript.
9 |
10 | For easier development, we have a couple of npm tasks:
11 |
12 | `npm run build` calls the typescript compiler and copies the HTML and JavaScript files to the `dist/` folder.
13 |
14 | `npm run builddocker` runs the build task and launches the project within a Node-RED docker container.
15 | In VS-Code you can also press Ctrl + Shift + B to run this task
16 |
17 | `npm run lint` calls eslint.
18 |
19 | `npm run createnode` to create a new JavaScript node. There is no template for a Typescript node yet.
20 |
21 | `npm test` runs the unit tests in the `test` folder.
22 |
23 | `npm testintegration` runs the integration tests in the `test-integration` folder. It starts the services in `test-integration/docker` and runs the integration tests against those services.
24 |
25 | # System requirements
26 | Developing is possible on Windows, Linux, and macOS. The IDE is up to you; we use VS-Code.
27 |
28 | - **Node.js & NPM** - One of the [LTS releases](https://nodejs.org/en/about/releases/)
29 | - **Docker & Docker Compose** - It's the easiest way to run the nodes with `npm run builddocker`
30 | - **Powershell**. On Windows, it is preinstalled. On Linux and macOS, you have to [install](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.1) it.
31 | - **rsync** - only required on Linux and macOS.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Dirnei
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # Zigbee2MQTT Nodes for Node-RED
2 |
3 | [](https://www.npmjs.com/package/node-red-contrib-zigbee2mqtt-devices)
4 | [](https://flows.nodered.org/node/node-red-contrib-zigbee2mqtt-devices)
5 | [](https://github.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/blob/master/LICENSE)
6 |
7 |
8 |
9 |
10 |
11 |
12 | This project contains Node-RED nodes that allow you to build Smart Home scenarios with your Zigbee devices connected to [ZigBee2MQTT](https://www.zigbee2mqtt.io/).
13 |
14 | 
15 |
16 | ## Available Nodes
17 |
18 | Many nodes are available right now, and there are still new node ideas that will be added in the near future. Here is a list of the currently available ones:
19 |
20 | - [generic-lamp](docs/nodes/generic-lamp.md)
21 | - [send-messages](docs/nodes/send-messages.md)
22 | - [override-nodes](docs/nodes/override-nodes.md)
23 | - [button-switch](docs/nodes/button-switch.md)
24 | - [scene-in](docs/nodes/scene-in.md)
25 | - [scene-selector](docs/nodes/scene-selector.md)
26 | - [ota-node](docs/nodes/ota-node.md)
27 | - [bridge-log](docs/nodes/bridge-log.md)
28 | - And many more for sensors and remotes...
29 |
30 | ## Getting started
31 |
32 | Have a look at [the getting started guide.](docs/getting-started.md). All you need are some already paired Zigbee lamps and an installed Node-RED. What are you waiting for? Find out now how easy it is to get started.
33 |
34 | ## Documentation
35 |
36 | The [documentation](docs/documentation.md) for the project is located in the `docs` folder.
37 |
38 | ## You need some addition help?
39 |
40 | Join the [Discord server](https://discord.gg/4qCMEhJ) for a more in depth support or problems that aren't metioned anywhere :)
41 |
42 | [](https://discord.gg/4qCMEhJ)
43 |
44 | ## You want to contribute?
45 |
46 | That's easy! Feel free to post suggestions, adding documentation or add new features. Every support is welcome!
47 |
48 | If you feel very generous and want to contribute with coffee, you sure can:
49 |
50 | [![Buy me a coffee][buymeacoffee-shield]][buymeacoffee]
51 |
52 | [buymeacoffee]: https://www.buymeacoffee.com/dirnei
53 | [buymeacoffee-shield]: https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png
54 |
55 | ## Changelog
56 |
57 | ### Release: `0.20.0` _3 Jan 2024_
58 |
59 | #### Features:
60 |
61 | - Add support for Ikea Styrbar (E2001) remote control
62 |
63 | #### Bug fixes:
64 |
65 | - Setting brightnes to 0 did not turn it off. Now it will override the state to "OFF"
66 | - Fixed Scenic Friend of Hue switch. Property names has changed between versions
67 |
68 | ### Release: `0.19.6` _13 Nov 2022_
69 |
70 | #### Features:
71 |
72 | #### Bug fixes:
73 | - Bridge did not connect in newer z2m versions because z2m changed the format of 'bridge/state' from string to JSON.
74 | - Shelly 2.5 did not connect to the broker, because it used an old configuration node.
75 | - Shelly 2.5 node did not unsubscribe from the old channel with no full redeploy.
76 |
77 | #### Behind the scenes
78 | - Add mocha and first-unit test examples
79 | - Add integration tests to test with a real MQTT broker
80 |
81 | ### Release: `0.19.5` - _14 Mar 2022_
82 |
83 | #### Bug fixes:
84 |
85 | - Hosting the Node-RED UI under a different root path than `http://localhost:1880/` resulted in failing web requests to load the device list. For example, when the UI was set to `http://localhost:1880/admin` or Node-RED was running as a Home Assistant plugin.
86 | - Fixed a bug where the configured device name gets deleted, when the device list couldn't be loaded. Further details in #105 and #119
87 |
88 | ### Release: `0.19.4` - _01 Jan 2022_
89 |
90 | Happy new year to all!
91 |
92 | #### Bug fixes:
93 | - @itupsk fixed the issue #114, where we forgot a null check on the ikea bugfix in the last release. Thanks a lot!
94 | - Also fixed the same issue in hue, scenic and sonoff devices
95 |
96 | ### Release: `0.19.3` - _30 Dec 2021_
97 |
98 | #### Features:
99 | - Documented additional settings for upgrading to mosquitto 2.0 in the getting started guide.
100 | - Documented Mirek scale.
101 |
102 | #### Bug fixes:
103 | - Not set property on Ikea Remote device caused a complete Node-Red restart
104 | - Not set property on Hue Remote device caused a complete Node-Red restart
105 | - Preventive measure: Check action for empty string in scenic remote and sonnoff buttons
106 |
107 | ### Release: `0.19.2` - 12 Mar 2021
108 |
109 | #### Bug fixes:
110 |
111 | - Local node-red installation with nodejs 12 had a problem to load the node-red dependency. Removed it for now as it is only used for logging.
112 |
113 | ### Release: `0.19.1` - 8 Mar 2021
114 |
115 | #### Bug fixes:
116 |
117 | - Better handling for invalid MQTT messages from z2m. Sometimes a required/expected property is missing or empty which caused an error.
118 | - Hue Dimmer switch crashed NodeRED because the power status with no action came with no action.
119 |
120 | #### Behind the scenes
121 |
122 | - Removed some unnecessary files from the package, reducing the size from 324 kB to 229 kB
123 | - Removed vulnerable dependencies
124 | - Updated and thinned out dependencies so the installation will be faster
125 | - Dev feature: Cleaned up npm build scripts so there are no warnings, and they behave the same on Windows, Linux, and macOS.
126 |
127 | ### Release: `0.19.0` - 23 Jan 2021
128 |
129 | #### Features:
130 | - Two new example flows, from the getting started guide
131 | - Documentation overview page
132 | - Documented examples
133 | - Show **switch to manual** button if the device-list request failed.
134 | - Added `deviceName` to the ouput of the climate-sensor. Resolves #75
135 | - Added `separateOuputs` option the the climate sensor. Resolves #75
136 |
137 | ### Older changelogs
138 |
139 | You can find all the release notes at the [Release-Page](https://github.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/releases)
140 |
--------------------------------------------------------------------------------
/_template.html:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
32 |
33 |
--------------------------------------------------------------------------------
/_template.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const utils = require("dist/lib/utils.js");
3 | const bavaria = utils.bavaria();
4 |
5 | function $$NAME_FUNC$$(config) {
6 | RED.nodes.createNode(this, config);
7 | var node = this;
8 |
9 | node.on("input", function (msg) {
10 | });
11 |
12 | node.on("close", function () {
13 | });
14 | }
15 |
16 | RED.nodes.registerType("$$NAME_NODE$$", $$NAME_FUNC$$);
17 | };
--------------------------------------------------------------------------------
/createNode.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory = $True)]
3 | [string]$NodeName,
4 | [Parameter(Mandatory = $True)]
5 | [string]$Label,
6 | [Parameter(Mandatory = $True)]
7 | [string]$FuncName,
8 | [Parameter(Mandatory = $True)]
9 | [string]$Category,
10 | [Parameter(Mandatory = $True)]
11 | [int]$Inputs,
12 | [Parameter(Mandatory = $True)]
13 | [int]$Outputs
14 | )
15 |
16 | Write-Host "============================================================================="
17 | Write-Host " Name of the node : $NodeName"
18 | Write-Host " Display-label value : $Label"
19 | Write-Host " Creating func name : $FuncName"
20 | Write-Host " Category : $Category"
21 | Write-Host " Inputs : $Inputs"
22 | Write-Host " Outputs : $Outputs"
23 | Write-Host "============================================================================="
24 |
25 | $nodeJsPath = "src/$NodeName.js"
26 | $nodeHtmlPath = "src/$NodeName.html"
27 |
28 | Write-Host " Copy templates..."
29 | Copy-Item -Path "_template.js" -Destination $nodeJsPath
30 | Copy-Item -Path "_template.html" -Destination $nodeHtmlPath
31 |
32 | Write-host " Replacing tokens..."
33 | (Get-Content -path $nodeJsPath -Raw).Replace('$$NAME_FUNC$$', $FuncName).Replace('$$NAME_NODE$$', $NodeName) | Set-Content -Path $nodeJsPath
34 | (Get-Content -path $nodeHtmlPath -Raw).Replace('$$NAME_LABEL$$', $Label).Replace('$$NAME_NODE$$', $NodeName).Replace('$$NAME_UPPER$$', $NodeName.ToUpper()).Replace('$$CATEGORY$$', $Category).Replace('$$INPUTS$$', $Inputs).Replace('$$OUTPUTS$$', $Outputs) | Set-Content -Path $nodeHtmlPath
35 | Write-host " Finished!"
36 | Write-Host "============================================================================="
37 | Write-Host
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 |
2 | version: "3.8"
3 |
4 | services:
5 | nodered:
6 | image: nodered/node-red:latest-12
7 | container_name: nodered_testing
8 | volumes:
9 | - ./debug/:/data
10 | ports:
11 | - "1880:1880"
12 | privileged: true
--------------------------------------------------------------------------------
/docs/config/bridge-config.md:
--------------------------------------------------------------------------------
1 | # Bridge configuration
2 |
3 | Define the connection to your MQTT-Broker and the base MQTT-Topic, which is defined in the zigbee2mqtt configuration.yaml.
4 |
5 | 
6 |
7 | ---
8 |
9 | ## Broker
10 |
11 | Detailed information about the mqtt-config can be found [here](mqtt-config.md)
12 |
13 | ---
14 |
15 | ## Base MQTT Topic
16 |
17 | This is defined in your zigbee2mqtt ```configuration.yaml```
18 |
19 | Default value is __zigbee2mqtt__
20 |
21 | ---
22 |
23 | ## Output Bridge logs
24 |
25 | If enabled, logs published to topic `zigbee2mqtt/bridge/logging` will be printed on the debug tab.
26 |
27 | > Default: off
28 |
29 | > Only messages with log-level warning and error will be written to the debug tab. The info log-level adds to much noise, because every published message will also be published under the same topic.
30 | (Current discussion about this topic: https://github.com/Koenkk/zigbee2mqtt/discussions/5633)
31 |
32 | ---
33 |
34 | ## Allow Device Status Refresh
35 |
36 | To reduce the amount of messages that will be sent, you can turn off the status refresh messages, as they only are used to update the state of the node and for nothing else.
37 |
38 | > Default: on
39 |
40 |
41 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/config/device-config.md:
--------------------------------------------------------------------------------
1 | # Device configuration
2 |
3 | 
4 |
5 | ## Bridge
6 |
7 | For more information see [bridge-config](../config/bridge-config.md)
8 |
9 | ## Device
10 |
11 | Select your device from the list. If nothing is shown here, you have probably just created a new bridge configuration, and you need to deploy it first in order to get a result here. If not, open a ticket and explain as detailed as possible what you have done :)
12 |
13 | ## Dimmable
14 |
15 | Define here if your lamp supports a brightness change.
16 |
17 | >If this is not checked, the generic-lamp configuration view will hide the brightness field from you.
18 |
19 | ## Temperate support
20 |
21 | Define here if your lamp supports color temperature.
22 |
23 | > If this is not checked, the generic-lamp configuration view will hide the color temperature field from you.
24 |
25 | ## Color support
26 |
27 | Define here if your lamp supports RGB colors.
28 |
29 | > If this is not checked, the generic-lamp configuration view will hide the RGB color fields from you.
30 |
31 | ## Experimental non z2m device support
32 |
33 | It is planned to also support non-z2m devices that can be controlled by MQTT. It is highly experimental and does not have a high priority right now. It will get more attention when the project is near version 1.0
34 |
35 | For now it is only supported in the [generic-lamp](../nodes/generic-lamp.md) and [send-messages](../nodes/send-messages.md) nodes.
36 |
37 | 
38 |
39 | ### Status topic
40 |
41 | Devices should send there state change to this topic.
42 |
43 | > Can be conpared with *zigbee2mqtt/friendly_name*
44 |
45 | ### Command topic
46 |
47 | When something is published to this topic, the device should change its state. It has to be able to parse the payload that is also be sent to zigbee2mqtt
48 |
49 | > Can be conpared with *zigbee2mqtt/friendly_name/set*
50 |
51 | ### Refresh topic
52 |
53 | When something is published to this topic, the device should respond with it's current state to the **status topic**. It has to be the same payload that zigbee2mqtt sends when a ZigBee device changes it state.
54 |
55 | > Can be conpared with *zigbee2mqtt/friendly_name/get*
56 |
57 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/config/img/bridge-config-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/config/img/bridge-config-config.png
--------------------------------------------------------------------------------
/docs/config/img/device-config-config-nonz2m-support.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/config/img/device-config-config-nonz2m-support.png
--------------------------------------------------------------------------------
/docs/config/img/device-config-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/config/img/device-config-config.png
--------------------------------------------------------------------------------
/docs/config/img/mqtt-config-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/config/img/mqtt-config-config.png
--------------------------------------------------------------------------------
/docs/config/mqtt-config.md:
--------------------------------------------------------------------------------
1 | # MQTT configuration
2 |
3 | Define the connection to your MQTT-Broker here. This is the same configuration used in the `mqtt-in` and `mqtt-out` node-red core nodes.
4 |
5 | 
6 |
7 |
8 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 |
3 | services:
4 | zigbee2mqtt:
5 | image: koenkk/zigbee2mqtt:latest
6 | container_name: z2m_composed
7 | volumes:
8 | - /run/udev:/run/udev:ro
9 | - ${PWD}/z2m:/app/data
10 | ports:
11 | - "8080:8080"
12 | environment:
13 | - TZ=Europe/Berlin
14 | devices:
15 | - device=/dev/ttyACM0
16 | network_mode: "host"
17 | restart: always
18 | privileged: true
19 | depends_on:
20 | - mosquitto
21 |
22 | mosquitto:
23 | image: eclipse-mosquitto
24 | container_name: mosquitto_composed
25 | volumes:
26 | - ${PWD}/mosquitto:/mosquitto
27 | ports:
28 | - "1883:1883"
29 | - "9001:9001"
30 | restart: always
31 |
32 | nodered:
33 | image: nodered/node-red
34 | container_name: nodered_composed
35 | volumes:
36 | - ${PWD}/nodered:/data
37 | ports:
38 | - "1880:1880"
39 | restart: always
40 | depends_on:
41 | - zigbee2mqtt
42 | - mosquitto
--------------------------------------------------------------------------------
/docs/documentation.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Zigbee2MQTT Nodes for Node-RED Documentation
6 |
7 | The Zigbee2MQTT Nodes for Node-REDs allow you to build Smart Home scenarios with your Zigbee
8 | devices connected to [ZigBee2MQTT](https://www.zigbee2mqtt.io/).
9 |
10 |
11 | ## 1. Getting started:
12 | The [**Getting started guide**](getting-started.md) contains an
13 | exemplary tutorial on setting up Node-RED, Zigbee2MQTT, etc., and how to define your first flow
14 | with the Zigbee2MQTT nodes for Node-RED. The guide will give you some examples of how to use the
15 | nodes and insight into the inner workings of the messages the library uses.
16 |
17 | If you already paired Zigbee lamps and an installed Node-RED, you can directly skip to the
18 | [Getting started guide: define your first flow](getting-started.md#define-your-first-flow)
19 | section.
20 |
21 |
22 | ## 2. Node documentation
23 |
24 | ### 2.1 Documentation per node
25 |
26 | - [generic-lamp](nodes/generic-lamp.md) Used to define which lamps you want to control.
27 | - [send-messages](nodes/send-messages.md) Prepares and sends the mqtt messages to the MQTT-Broker.
28 | - [override-nodes](nodes/override-nodes.md) Overrides the properties that are set a generic-lamp.
29 | - [override-state](nodes/override-state.md) Overrides the `ON`/`OFF` state.
30 | - [override-brightness](nodes/override-brightness.md) Overrides the brightness of a lamp.
31 | - [override-temperature](nodes/override-temperature.md) Overrides light temperature of a lamp.
32 | - [override-color](nodes/override-color.md) Overrides the light color of a lamp.
33 | - [override-action](nodes/override-action.md) Modify brightness/color over time or in steps.
34 | - [button-switch](nodes/button-switch.md) Is used after a remote node to redirect the message to a seperate output.
35 | - [scene-in](nodes/scene-in.md) Configures a scene which can be activated via the scene-selector.
36 | - [scene-selector](nodes/scene-selector.md) Select scenes in a defined order.
37 | - [device-satus](nodes/device-status.md) Retrieve notifications when a device status changes.
38 | - [get-lamp-state](nodes/get-lamp-state.md) Retrieve the current state of a lamp.
39 | - [ota-node](nodes/ota-node.md) Used to start OTA updates.
40 | - [bridge-log](nodes/bridge-log.md) Filter logs of the Zigbee2MQTT bridge.
41 | - [climate-sensor](nodes/climate-sensor.md) Used to get values from a climate sensor.
42 |
43 | > Note: There are a few nodes for sensors and remotes that have not been documented in the wiki yet, but you can read about them in the internal help documentation.
44 |
45 | ### 2.2 Configs
46 | - [bridge-config](config/bridge-config.md) Select your MQTT-Broker and the base MQTT topic used for Zigbee2MQTT.
47 | - [mqtt-config](config/mqtt-config.md) Configuret the connection and credentials a your MQTT-Broker.
48 | - [device-config](config/device-config.md) Configure devices and capabilities to use them with the nodes.
49 |
50 |
51 | ## 3. Examples
52 |
53 | The examples are already included, if you installed the package.
54 |
55 | Open `Node-RED Menu > Import > Examples > node-red-contrib-zigbee2mqtt-devices` to import the example flows.
56 |
57 | ### Getting started
58 | [**One lamp On/Off**](../examples/getting-started/one-lamp-on-off.json) is a simple example to turn one lamp on and off with two inject nodes.
59 |
60 | 
61 |
62 |
63 | [**Two lamps override**](../examples/getting-started/two-lamps-override.json) demonstrates how to use override nodes to control multiple lamps.
64 |
65 | 
66 |
67 | ### One Time Update
68 | [**OTA over night**](../examples/ota/ota_over_night.json) enables the auto update between 00:00 and 04:00.
69 |
70 | 
71 |
72 | ### Scenes
73 | [**Basic scenes**](../examples/scenes/basic_scene.json) switches scenes with the scene selector.
74 |
75 | 
--------------------------------------------------------------------------------
/docs/img/0-18-update-delete-old-nodes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/0-18-update-delete-old-nodes.png
--------------------------------------------------------------------------------
/docs/img/0-18-update-reconfigue-bridge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/0-18-update-reconfigue-bridge.png
--------------------------------------------------------------------------------
/docs/img/0.18-update-new-mqttt-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/0.18-update-new-mqttt-config.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow01-send-messages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow01-send-messages.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow02-bridge-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow02-bridge-config.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow03-mqtt-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow03-mqtt-config.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow04-connected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow04-connected.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow05-generic-lamp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow05-generic-lamp.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow06-hue-device-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow06-hue-device-config.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow07-brightness-color-hue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow07-brightness-color-hue.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow08-inject-node.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow08-inject-node.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow09-toggle-device-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow09-toggle-device-config.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow10-toggle.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow10-toggle.gif
--------------------------------------------------------------------------------
/docs/img/getting-started-flow11-off-device-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow11-off-device-config.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow12-on-off-flow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow12-on-off-flow-1.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow12-on-off-flow-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow12-on-off-flow-2.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow13-on-off-flow-3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow13-on-off-flow-3.gif
--------------------------------------------------------------------------------
/docs/img/getting-started-flow14-second-lamp-paralell.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow14-second-lamp-paralell.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow15-second-lamp-series.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow15-second-lamp-series.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow16-override-nodes.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow16-override-nodes.gif
--------------------------------------------------------------------------------
/docs/img/getting-started-flow17-override-multiple.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow17-override-multiple.gif
--------------------------------------------------------------------------------
/docs/img/getting-started-flow18-inject-override.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow18-inject-override.png
--------------------------------------------------------------------------------
/docs/img/getting-started-flow19-inject-override-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-flow19-inject-override-flow.png
--------------------------------------------------------------------------------
/docs/img/getting-started-install-palette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-install-palette.png
--------------------------------------------------------------------------------
/docs/img/getting-started-z2m-renamed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/getting-started-z2m-renamed.png
--------------------------------------------------------------------------------
/docs/img/overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/img/overview.png
--------------------------------------------------------------------------------
/docs/nodes/bridge-log.md:
--------------------------------------------------------------------------------
1 | # Bridge log
2 |
3 | The bridge log node provides an easy way to filter logs that are published into the
4 | `zigbee2mqtt/bridge/log` topic. You configure the types you are interested in, and the node creates one output for each type. This can be helpful if you only want to see specific logs (with the debug node) or perform actions based on the logs.
5 |
6 | In this example, only the log types `pairing` and `device_announced` are selected. The `pairing` logs trigger a web request, the `device_announced` logs are sent to the debug node.
7 |
8 | 
9 |
10 |
11 |
12 | ## Configuration
13 |
14 | In the configuration, you select the log types you want to have an output created.
15 | The following configuration creates the node in the example above with two outputs.
16 |
17 | 
18 |
19 | The bridge log node supports all 28 types. For more details about the types, look at the description in the configuration window or the [Zigbee2MQTT MQTT Topics and Message structure documentation](https://www.zigbee2mqtt.io/information/mqtt_topics_and_message_structure.html#zigbee2mqttbridgelog).
20 |
21 | ### Bridge
22 |
23 | The Zigbee2MQTT bridge. For more information see [bridge-config](../config/bridge-config.md).
24 |
25 | ### Consolidated output
26 |
27 | The consolidate output option is attractive if you want all or certain log types, but every log message results in the same action. If the consolidate output option is selected, all the selected log types will be sent over a single output.
28 |
29 | The following example sends the logs with the type `pairing` or `device_connected` into the debug node.
30 |
31 | 
32 |
33 | 
34 |
35 |
36 |
37 | ## Message format
38 |
39 | Zigbee2MQTT sends the logs to the `zigbee2mqtt/bridge/log` topic in the following format:
40 | ``` json
41 | { "type": "TYPE", "message": "MESSAGE" }
42 | ```
43 | The bridge log node unwraps the message in the `message` property and put's it directly into the `payload`. The type is put into `action.name`. The payload is not necessarily a string - e.g., the `devices` log contains a list of devices.
44 |
45 |
46 | ### Example
47 | The following MQTT message from Zigbee2MQTT:
48 |
49 | ``` json
50 | {
51 | "type": "device_announced",
52 | "message": "announce"
53 | }
54 | ```
55 |
56 | Is converted into this Node-RED message object as it leaves the bridge log node.
57 | ``` json
58 | {
59 | "action": {
60 | "name": "device_announced",
61 | "description": "Logs with the type: device_announced"
62 | },
63 | "payload": "announce",
64 | "_msgid": "770eadcb.231f54"
65 | }
66 | ```
67 |
68 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/button-switch.md:
--------------------------------------------------------------------------------
1 | # Button switch
2 |
3 | The button switch node is used after a remote node. It redirects the message to it's own seperate output by the ```button_type``` property of the message payload.
4 |
5 | The are currently the following ```button_type``` values supported:
6 |
7 | - pressed
8 | - hold
9 | - released
10 | - double (Only the SONOFF touch button supports this type right now)
11 |
12 | 
13 |
14 | ## Configuration
15 |
16 | To use the button switch, you have to enable the outputs you need. You also can define a custom ```msg.payload``` for each ```button_type```. You can define the following types:
17 |
18 | - string
19 | - number
20 | - boolean
21 | - json
22 |
23 | 
24 |
25 | ## Repeatedly send hold message
26 |
27 | In some cases, you may want to send multiple messages while a button is held down. If that is the case, you can enable it after you enabled the output for ```hold```. The ```released``` output has not to be enabled for this to work.
28 |
29 | > To not end up in an endless loop, you have to specify a ```maximum amount of messages```! If your amount is 0 or less, it will not produce any output!
30 |
31 | 
32 |
33 |
34 | ## Support remote devices
35 |
36 | - Ikea Dimmer
37 | - Ikea Remote
38 | - Sonoff Touch Button
39 | - Scenic Friend of Hue
40 | - Hue Dimmer switch
41 |
42 | ### Future implementations
43 |
44 | - Hue Smart button
45 | - Tint Color remote
46 |
47 | Or join our [Discord server](https://discord.gg/4qCMEhJ) and suggest a device we should support :)
48 |
49 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/climate-sensor.md:
--------------------------------------------------------------------------------
1 | # Climate sensor
2 |
3 | The climate sensor node can be used to retrieve data from a climate sensor. In the configuration, you can define which values you want to display in the status of the node. If the **separate outputs** option is enabled, the selected properties each get their own output, which only outputs the value of the property.
4 |
5 | ## Configuration
6 |
7 | 
8 |
9 | ### Bridge
10 |
11 | For more information see [bridge-config](../config/bridge-config.md).
12 |
13 | ### Device
14 |
15 | Select the zigbee2mqtt device you want to listen to.
16 |
17 | ### Temperature
18 |
19 | Displays the temperature in the status of the node.
20 |
21 | ### Pressure
22 |
23 | Displays the pressure in the status of the node.
24 |
25 | ### Humidity
26 |
27 | Displays the humidity in the status of the node.
28 |
29 | ### CO2
30 |
31 | Displays the CO2 in the status of the node.
32 |
33 | ### Separate outputs
34 |
35 | Separates the selected properties into different outputs. If this is enabled, the first output still outputs the raw MQTT message as well.
36 |
37 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/device-status.md:
--------------------------------------------------------------------------------
1 | # Device Status
2 |
3 | A device status node can be used to retrieve notifications whenever a device status was updated. In the configuration, you can define and select your device which you want to listen to. Note that only zigbee router devices can be selected.
4 |
5 | ## Configuration
6 |
7 | 
8 |
9 | ### Bridge
10 |
11 | For more information see [bridge-config](../config/bridge-config.md).
12 |
13 | ### Device
14 |
15 | Select the zigbee2mqtt device you want to listen to.
16 |
17 | ### Generic MQTT Device (experimental)
18 |
19 | 
20 |
21 | When checking the checkbox, experimental mode is activated. For further information, see [device-config](../config/device-config.md).
22 |
23 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/generic-lamp.md:
--------------------------------------------------------------------------------
1 | # Generic lamp
2 |
3 | A genric lamp is used to define which lamps you want to control. In the configuration you can define and select your device and set all states that the devices supports. When you done setting your state you have to put a [send-messages](send-messages.md) node at the end.
4 |
5 | 
6 |
7 | # Configuration
8 |
9 | 
10 |
11 | ## Device
12 |
13 | For more information see [device-config](../config/device-config.md)
14 |
15 | ## Bridge
16 |
17 | For more information see [bridge-config](../config/bridge-config.md)
18 |
19 | ## State
20 |
21 | Set the desired state you want to send to the lamp. Following values are available:
22 |
23 | - ON
24 | - OFF
25 | - TOGGLE
26 |
27 | ## Delay
28 |
29 | The delay is used to delay the message befor it will be sent to the mqtt broker. This can be used for animation. It is defined in milliseconds. The order of your lamps withing the flow is important.
30 |
31 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/get-lamp-state.md:
--------------------------------------------------------------------------------
1 | # Get lamp state
2 |
3 | A get lamp state node can be used to retrieve the current state of a lamp (which was configured as [device-config](../config/device-config.md) previously) on message input. In the configuration, you can define and select your device which you want to listen to.
4 |
5 | ## Configuration
6 |
7 | 
8 |
9 | ### Bridge
10 |
11 | For more information see [bridge-config](../config/bridge-config.md).
12 |
13 | ### Device
14 |
15 | Select the lamp you want to retrieve the state from. For more information see [device-config](../config/device-config.md).
16 |
17 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/img/bridge-log-config-consolidate-output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/bridge-log-config-consolidate-output.png
--------------------------------------------------------------------------------
/docs/nodes/img/bridge-log-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/bridge-log-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/bridge-log-flow-consolidate-output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/bridge-log-flow-consolidate-output.png
--------------------------------------------------------------------------------
/docs/nodes/img/bridge-log-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/bridge-log-flow.png
--------------------------------------------------------------------------------
/docs/nodes/img/button-switch-config-hold-repeat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/button-switch-config-hold-repeat.png
--------------------------------------------------------------------------------
/docs/nodes/img/button-switch-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/button-switch-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/button-switch-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/button-switch-flow.png
--------------------------------------------------------------------------------
/docs/nodes/img/climate-sensor-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/climate-sensor-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/device-status-config-experimental.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/device-status-config-experimental.png
--------------------------------------------------------------------------------
/docs/nodes/img/device-status-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/device-status-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/generic-lamp-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/generic-lamp-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/generic-lamp-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/generic-lamp-flow.png
--------------------------------------------------------------------------------
/docs/nodes/img/get-lamp-state-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/get-lamp-state-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-autoUpdate-msg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-autoUpdate-msg.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-config-blacklist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-config-blacklist.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-context-updates-available.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-context-updates-available.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-example-flow-over-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-example-flow-over-night.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-update-available.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-update-available.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-update-finished.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-update-finished.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-update-in-progress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-update-in-progress.png
--------------------------------------------------------------------------------
/docs/nodes/img/ota-node-update-progress-output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/ota-node-update-progress-output.png
--------------------------------------------------------------------------------
/docs/nodes/img/override-action-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/override-action-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/override-brightness-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/override-brightness-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/override-color-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/override-color-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/override-nodes-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/override-nodes-example.png
--------------------------------------------------------------------------------
/docs/nodes/img/override-state-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/override-state-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/override-temperature-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/override-temperature-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/scene-in-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/scene-in-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/scene-selector-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/scene-selector-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/scene-selector-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/scene-selector-example.png
--------------------------------------------------------------------------------
/docs/nodes/img/send-messages-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/send-messages-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/shelly25-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/shelly25-config.png
--------------------------------------------------------------------------------
/docs/nodes/img/shelly25-listen-input.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/shelly25-listen-input.png
--------------------------------------------------------------------------------
/docs/nodes/img/shelly25-toggle-relay-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/shelly25-toggle-relay-settings.png
--------------------------------------------------------------------------------
/docs/nodes/img/shelly25-toggle-relay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirnei/node-red-contrib-zigbee2mqtt-devices/65c1898ad79b7c0209ed9a08f7b1c33a77241c9f/docs/nodes/img/shelly25-toggle-relay.png
--------------------------------------------------------------------------------
/docs/nodes/ota-node.md:
--------------------------------------------------------------------------------
1 | # OTA node
2 |
3 | For basic information about OTA updates via zigbee2mqtt, look at the documentation [here](https://www.zigbee2mqtt.io/information/ota_updates.html).
4 |
5 | > If you use our dashboard, have a look at the end of the flow. The node is used there to auto-update overnight.
6 |
7 | The OTA-node is used to start OTA updates. The updates can be executed manually or automatically.
8 |
9 | ## Outputs
10 |
11 | This node has three outputs. **start/end**, **progress** and **queue changed**. The name of the output matches the trigger of the message. E.g., when the progress of the current update changes, a message will be sent to output 2 (Label: "progress")
12 |
13 | ## Manual update
14 |
15 | To start the update process manually, you have to send a ```msg``` to the input of the node with the following structure:
16 |
17 | ``` js
18 | {
19 | payload: {
20 | device: "friendly_name"
21 | }
22 | }
23 | ```
24 |
25 | ## Automatic update
26 |
27 | ### Always on
28 |
29 | If you want the feature always available, you can turn it on in the config.
30 |
31 | 
32 |
33 | ### Turn on by message
34 |
35 | It is also possible to turn this feature **on** and **off** by sending a message to the input with the following structure:
36 |
37 | ``` js
38 | {
39 | payload: {
40 | autoUpdate: true
41 | }
42 | }
43 | ```
44 |
45 | A real-world example would be to allow the auto-update only during the night. To archive this, you could use two **inject nodes** to send a message at a specific time — one to turn the auto-update on, one to turn it off.
46 |
47 | You can copy-paste the flow example below from the example folder in the repo.
48 |
49 | 
50 |
51 | ## Blacklist
52 |
53 | You may want to update some lamps only manually for various reasons. For example, the Ikea lamps turn on for a short time after the installation process succeeded. If this lamp is over your head while you are asleep, it might wake you. To prevent those situations, you can add those lamps to a blacklist. All lamps on the blacklist can only be updated manually with a **msg** sent to the input (see description above), or you use our dashboard you find in the dashboard folder at the root of the repo.
54 |
55 | 
56 |
57 | ## Some screenshots
58 |
59 | ### Example 1: "Updates available"
60 |
61 | 
62 |
63 | ### Example 2: "Node-context from contextview on the sidebar"
64 |
65 | 
66 |
67 | ### Example 3: "Update in progress"
68 | 
69 |
70 | ### Example 4: "Debug output"
71 |
72 | 
73 | 
74 |
75 | ## Nice to know
76 | > Only one OTA-update node per bridge is allowed. If more than one is deployed, only the first one will work. All others will show an error status.
77 |
78 | ## Example
79 |
80 | The [**OTA over night example flow**](../../examples/ota/ota_over_night.json) enables the auto update between 00:00 and 04:00 and is included in this package.
81 |
82 | Import it via: `Node-RED Menu > Import > Examples > node-red-contrib-zigbee2mqtt-devices > ota > ota_over_night`
83 |
84 | 
85 |
86 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/override-action.md:
--------------------------------------------------------------------------------
1 | # Override action
2 |
3 | Instead of setting a brightness, color_temp, hue or saturation it is also possible to:
4 |
5 | __move__ - this will automatically move the value over time, to stop send value 0.
6 |
7 | __step__ - this will increment/decrement the current value by the given one.
8 | The direction of move and step can be either up or down, provide a negative value to move/step down, a positive value to move/step up.
9 |
10 | 
11 |
12 | ### Notes
13 |
14 | > brightness move/step will stop at the minimum brightness and won't turn on the light when it's off.
15 |
16 | > When a action is set in the current flow, other changes will be ignored. It is only possible to do one action at a time.
17 |
18 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/override-brightness.md:
--------------------------------------------------------------------------------
1 | # Override brightness
2 |
3 | Brightness values can be set between 254 (100%) and 0 (0%). All lamps in the flow will be set to the same value defined in this node.
4 |
5 | 
6 |
7 | ## Example flow
8 |
9 | 
10 |
11 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/override-color.md:
--------------------------------------------------------------------------------
1 | # Override color
2 |
3 | RGB values can be set between 0 and 255. All lamps in the flow will be set to the same value defined in this node.
4 |
5 | 
6 |
7 | ## Example flow
8 |
9 | 
10 |
11 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/override-nodes.md:
--------------------------------------------------------------------------------
1 | # Override nodes
2 |
3 | There are multiple override node available. The behaviour of each node is still the same. It overrides the properties that are set in a [genric-lamp](generic-lamp.md) node. If a device does not support the properties that the override node will set, it will have no effect. The override is applied to all *generic-lamp* nodes in the flow.
4 |
5 | ## Available nodes
6 |
7 | - [override-state](override-state.md) Overrides the `ON`/`OFF` state.
8 | - [override-brightness](override-brightness.md) Overrides the brightness of a lamp.
9 | - [override-temperature](override-temperature.md) Overrides light temperature of a lamp.
10 | - [override-color](override-color.md) Overrides the light color of a lamp.
11 | - [override-action](override-action.md) Modify brightness/color over time or in steps.
12 |
13 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/override-state.md:
--------------------------------------------------------------------------------
1 | # Override state
2 |
3 | The state can be set to `ON`, `OFF` or `TOGGLE`. Be carefull with `TOGGLE` because when not all lamps in your flow are in the same state, the ones that are on turn off and vice versa.
4 |
5 | 
6 |
7 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/override-temperature.md:
--------------------------------------------------------------------------------
1 | # Override temperature
2 |
3 | Used to set the color temperature for all lamps in the flow to the same value.
4 |
5 | 
6 |
7 | Color temperature in Reciprocal MegaKelvin, a.k.a. Mirek scale.
8 |
9 | ```
10 | Mirek = 1,000,000 / Color Temperature in Kelvin
11 | ```
12 |
13 | > Values typically range between 50 and 400. The higher the value, the warmer the color.
14 |
15 | | Kelvin | Mirek |
16 | |---------------|-------|
17 | | 2400 K | 416 |
18 | | 2700 K | 370 |
19 | | 3000 K | 333 |
20 | | 3200 K | 312 |
21 | | 3350 K | 299 |
22 | | 5000 K | 200 |
23 | | 6200 K | 161 |
24 | | 6500 K | 155 |
25 | | 8000 K | 125 |
26 | | 10000 K | 100 |
27 |
28 | ## Example flow
29 |
30 | 
31 |
32 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/scene-in.md:
--------------------------------------------------------------------------------
1 | # Scene
2 |
3 | A scene-in node configures a scene which can be activated via the [scene-selector](scene-selector.md).
4 |
5 | ## Configuration
6 |
7 | 
8 |
9 | ### Name
10 |
11 | Changes the display label of the node.
12 |
13 | ### Scene
14 |
15 | The name of the scene which can be used within the [scene-selector](scene-selector.md)s ```set``` command.
16 |
17 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/scene-selector.md:
--------------------------------------------------------------------------------
1 | # Scene selector
2 |
3 | A scene selector can be used to select scenes in a defined order. When the selector receives a message with a given ```msg.command``` property, it will select the corresponding scene and trigger its' activation. For more information on scenes see [scene-in](scene-in.md).
4 |
5 | ## Input
6 |
7 | You can choose among the following commands:
8 | - ```next``` activates the next scene
9 | - ```previous``` activates the previous scene
10 | - ```set``` directly activate a given scene. To specifiy which scene, a ```msg.scene``` property has to be set. It can either contain an integer corresponding to the array index of the scene or can contain a string corresponding to the "Scene" property of the [scene-in](scene-in.md) node.
11 |
12 | ## Configuration
13 |
14 | 
15 |
16 | ### Scenes
17 |
18 | Using the dropdown and the "add scene" button, you can generate a sequence of scenes. A scene can be removed by pressing the "X" button.
19 |
20 | ### Wrap around
21 |
22 | This property decides, which behaviour the selector has, whenever the selected scene index reaches the beginning (when stepping down) or the end (when stepping up) of the scene sequence.
23 | - If ```true```:
24 | | Command | Current scene | New scene |
25 | |----------|---------------|-----------|
26 | | previous | first | last |
27 | | next | last | first |
28 | - If ```false```:
29 | | Command | Current scene | New scene |
30 | |----------|---------------|-----------|
31 | | previous | first | first |
32 | | next | last | last |
33 |
34 | ### Changed output only
35 |
36 | If set to true, the node only outputs a scene selection if the selected scene has changed. If e.g. a ```set``` command with the same index is sent multiple time consecutively, the scene will be only triggered once.
37 |
38 | ## Example
39 |
40 | The [**basic scene example flow**](../../examples/scenes/basic_scene.json) is included in the package.
41 |
42 | Import it via: `Node-RED Menu > Import > Examples > node-red-contrib-zigbee2mqtt-devices > scenes > basic_scene`
43 |
44 |
45 | 
46 |
47 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/send-messages.md:
--------------------------------------------------------------------------------
1 | # Send messages
2 |
3 | A send message node is needed at the and of a flow that should control zigbee2mqtt devices.
4 | It prepares and sends the mqtt messages to the MQTT-Broker.
5 |
6 | 
7 |
8 | ## Configuration
9 |
10 | There is not much configuration needed for this node. Just select or configure a new [bridge configuration](../config/bridge-config.md)
11 |
12 | If you want to change the label of the node on flow you can set a name.
13 |
14 | 
15 |
16 | [*← back to the index*](../documentation.md)
--------------------------------------------------------------------------------
/docs/nodes/shelly-25-node.md:
--------------------------------------------------------------------------------
1 | # Shelly 2.5
2 |
3 | The shelly 2.5 node controls the relays on a shelly and receives input, relay, and status changes.
4 |
5 | Tested with the following Shellies:
6 | - Shelly 1
7 | - Shelly 2.5
8 |
9 | ## Configuration
10 |
11 | 
12 |
13 | ### Broker
14 |
15 | For more information see [mqtt-config](../config/mqtt-config.md)
16 |
17 | ### Device
18 |
19 | The device section configures a shelly's MQTT topic and, optionally, its friendly name.
20 | For example, `shellies/my-device`, the shelly node will subscribe and publish on this topic.
21 |
22 | ### Channel
23 |
24 | The channel configures which relay will be triggered when the node receives an input. Furthermore, the node only publishes relay and input changes for the selected input.
25 | Possible options are:
26 | - `Channel 1`: Switches and subscribes to channel 1 (MQTT topic `relay/0` and `input/0`).
27 | - `Channel 2`: Switches and subscribes to channel 2 (MQTT topic `relay/1` and `input/1`).
28 | - `Both`: Switches and subscribes to channels 1 and 2.
29 |
30 | For the shelly 1, one should stick to channel one, as it has only one input and relay.
31 |
32 | ### State
33 | The state is used to control the relay when the node receives an input.
34 | - `On`: Switches the relay for the selected channel to on.
35 | - `Off`: Switches the relay for the selected channel to off.
36 | - `Toggle`: Switches the relay for the selected channel on if it is off and vice versa.
37 |
38 | ### Enable Input
39 | This option enables the input connector on the node. Only when it is enabled is it possible to switch the relays.
40 |
41 | ### Custom payload
42 | The custom payload allows you to define a specific payload to be published when the shelly's input state changes since the default are `0` and `1`, which might not be too helpful, primarily if two channels are used. For example:
43 | - Payload 1: Input one was triggered
44 | - Payload 2: Input zero was triggered
45 |
46 |
47 | ## Usage
48 |
49 | ### Control a shelly's relay
50 |
51 | On input, the shelly 2.5 node prepares a payload sent to the shelly by [send-messages](send-messages.md) via MQTT.
52 |
53 | The inject node triggers the shelly node to create a payload. The shelly node is configured to toggle its state between on and off. Moreover, only channel one is selected. The send-messages node is configured to use an MQTT broker as well as a zigbee2mqtt topic. The zigbee2mqtt topic is ignored for shellies, and they publish at the configured topic, usually `shellies/my-device`.
54 |
55 |
56 | 
57 |
58 | 
59 |
60 | To try it, have a look at the example: [shelly25-toggle-relay.json](../../examples/shelly-25/shelly25-toggle-relay.json)
61 |
62 |
63 | ### Receive input or relay changes from a shelly
64 |
65 | When the relay actuates, or a payload is sent to the first output to the 'Log shelly relay' debug node.
66 | When the input changes, a payload is sent to the second output to the 'Log shelly input' debug node.
67 |
68 | 
69 |
70 | To try it, have a look at the example: [shelly25-listen-to-input.json](../../examples/shelly-25/shelly25-listen-to-input.json)
71 |
72 |
73 | [*← back to the index*](../documentation.md)
74 |
--------------------------------------------------------------------------------
/examples/getting-started/one-lamp-on-off.json:
--------------------------------------------------------------------------------
1 | [{"id":"2dad0b7b.81c984","type":"send-messages","z":"6d72d1fc.47431","name":"","bridge":"c34db5f4.463458","x":500,"y":200,"wires":[]},{"id":"7e359dfa.509e14","type":"inject","z":"6d72d1fc.47431","name":"On","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":140,"wires":[["2ad3c58c.a46f2a"]]},{"id":"26e69b6b.c47ec4","type":"inject","z":"6d72d1fc.47431","name":"Off","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":260,"wires":[["e9988615.fb6cd8"]]},{"id":"e1fb381.4bb73c8","type":"comment","z":"6d72d1fc.47431","name":"State: ON","info":"","x":260,"y":100,"wires":[]},{"id":"9a5f81fb.3c992","type":"comment","z":"6d72d1fc.47431","name":"State: OFF","info":"","x":260,"y":220,"wires":[]},{"id":"2ad3c58c.a46f2a","type":"generic-lamp","z":"6d72d1fc.47431","device":"b9730eda.9ab35","name":"Ikea Trådfri 806 lm","state":"ON","brightness":"254","temperature":50,"red":0,"green":0,"blue":0,"transition":"2","delay":0,"x":290,"y":140,"wires":[["2dad0b7b.81c984"]]},{"id":"e9988615.fb6cd8","type":"generic-lamp","z":"6d72d1fc.47431","device":"b9730eda.9ab35","name":"Ikea Trådfri 806 lm","state":"OFF","brightness":"254","temperature":50,"red":0,"green":0,"blue":0,"transition":"2","delay":0,"x":290,"y":260,"wires":[["2dad0b7b.81c984"]]},{"id":"c34db5f4.463458","type":"zigbee2mqtt-bridge-config","name":"Zigbee2MQTT Intel NUC","broker":"cbf4dc5b.3a4d7","baseTopic":"zigbee2mqtt","enabledLogging":false,"allowDeviceStatusRefresh":false},{"id":"b9730eda.9ab35","type":"zigbee2mqtt-device-config","name":"Ikea Trådfri 806 lm","bridge":"c34db5f4.463458","deviceName":"Ikea Trådfri 806lm","brightnessSupport":true,"temperatureSupport":false,"colorSupport":false,"genericMqttDevice":false,"statusTopic":"","commandTopic":"","refreshTopic":""},{"id":"cbf4dc5b.3a4d7","type":"mqtt-broker","name":"MQTT Broker","broker":"192.168.178.49","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
--------------------------------------------------------------------------------
/examples/getting-started/two-lamps-override.json:
--------------------------------------------------------------------------------
1 | [{"id":"5f37476b.814558","type":"send-messages","z":"24f36957.60dbd6","name":"","bridge":"c34db5f4.463458","x":1020,"y":180,"wires":[]},{"id":"f86a33a4.b9a0a","type":"generic-lamp","z":"24f36957.60dbd6","device":"6a600609.e54ca8","name":"Phillips Hue white ambience","state":"ON","brightness":"30","temperature":"153","red":0,"green":0,"blue":0,"transition":"2","delay":0,"x":560,"y":180,"wires":[["f99218ad.8866b8"]]},{"id":"74938da8.28c264","type":"inject","z":"24f36957.60dbd6","name":"On","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":160,"wires":[["a6abb9ca.9847c8"]]},{"id":"f051fef9.5fbb3","type":"inject","z":"24f36957.60dbd6","name":"Off","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":200,"wires":[["3d8ddf9c.5ebca"]]},{"id":"f99218ad.8866b8","type":"generic-lamp","z":"24f36957.60dbd6","device":"b9730eda.9ab35","name":"Ikea Trådfri 806 lm","state":"ON","brightness":"254","temperature":50,"red":0,"green":0,"blue":0,"transition":"2","delay":0,"x":810,"y":180,"wires":[["5f37476b.814558","57d4f951.9e8498"]]},{"id":"a6abb9ca.9847c8","type":"override-state","z":"24f36957.60dbd6","name":"override-state: On","state":"ON","x":310,"y":160,"wires":[["f86a33a4.b9a0a"]]},{"id":"3d8ddf9c.5ebca","type":"override-state","z":"24f36957.60dbd6","name":"override-state: Off","state":"OFF","x":310,"y":200,"wires":[["f86a33a4.b9a0a"]]},{"id":"9ef91f6a.b3c26","type":"inject","z":"24f36957.60dbd6","name":"Relax mode","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":100,"wires":[["78ef023d.a3e8dc"]]},{"id":"78ef023d.a3e8dc","type":"override-brightness","z":"24f36957.60dbd6","name":"override-brightness: 50% (127)","brightness":"127","x":350,"y":100,"wires":[["95023793.a57d38"]]},{"id":"95023793.a57d38","type":"override-temperature","z":"24f36957.60dbd6","name":"override-temperature: (333)","temperature":333,"x":640,"y":100,"wires":[["a6abb9ca.9847c8"]]},{"id":"236ac64a.d73c2a","type":"inject","z":"24f36957.60dbd6","name":"Work mode","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"override\":{\"brightness\":\"255\",\"temperature\":100,\"state\":\"ON\"}}","payloadType":"json","x":350,"y":260,"wires":[["f86a33a4.b9a0a"]]},{"id":"57d4f951.9e8498","type":"debug","z":"24f36957.60dbd6","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":990,"y":240,"wires":[]},{"id":"c34db5f4.463458","type":"zigbee2mqtt-bridge-config","name":"Zigbee2MQTT Intel NUC","broker":"cbf4dc5b.3a4d7","baseTopic":"zigbee2mqtt","enabledLogging":false,"allowDeviceStatusRefresh":false},{"id":"6a600609.e54ca8","type":"zigbee2mqtt-device-config","name":"Phillips Hue white ambience","bridge":"c34db5f4.463458","deviceName":"Lamp Terrace","brightnessSupport":true,"temperatureSupport":true,"colorSupport":false,"genericMqttDevice":false,"statusTopic":"","commandTopic":"","refreshTopic":""},{"id":"b9730eda.9ab35","type":"zigbee2mqtt-device-config","name":"Ikea Trådfri 806 lm","bridge":"c34db5f4.463458","deviceName":"Ikea Trådfri 806lm","brightnessSupport":true,"temperatureSupport":false,"colorSupport":false,"genericMqttDevice":false,"statusTopic":"","commandTopic":"","refreshTopic":""},{"id":"cbf4dc5b.3a4d7","type":"mqtt-broker","name":"MQTT Broker","broker":"192.168.178.49","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
--------------------------------------------------------------------------------
/examples/ota/ota_over_night.json:
--------------------------------------------------------------------------------
1 | [{"id":"d3d215cf.2efe18","type":"inject","z":"e5f179a9.dd2658","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"00 00 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"{\"autoUpdate\":true}","payloadType":"json","x":620,"y":1520,"wires":[["4a909756.e271c8"]]},{"id":"a0c0155d.aa5a38","type":"inject","z":"e5f179a9.dd2658","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"00 04 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"{\"autoUpdate\":false}","payloadType":"json","x":620,"y":1560,"wires":[["4a909756.e271c8"]]},{"id":"4a909756.e271c8","type":"ota-update","z":"e5f179a9.dd2658","name":"","bridge":"8d961b4d.cfe858","autoUpdate":false,"verboseLogging":false,"x":830,"y":1520,"wires":[["7b9d12.9410c2f"],["538eb538.6377ac","c70fc71.94ab938"]]},{"id":"8d961b4d.cfe858","type":"zigbee2mqtt-bridge-config","z":"","name":"raspberry","baseTopic":"zigbee2mqtt","broker":"mqtt://localhost:1883","requireLogin":false}]
--------------------------------------------------------------------------------
/examples/scenes/basic_scene.json:
--------------------------------------------------------------------------------
1 | [{"id":"2746ea7f.991456","type":"debug","z":"f99a795c.3e73c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":810,"y":140,"wires":[]},{"id":"272ec6fa.1c208a","type":"inject","z":"f99a795c.3e73c8","name":"next","props":[{"p":"command","v":"next","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":80,"wires":[["ad17b0b6.40d5c"]]},{"id":"7f911100.72e77","type":"inject","z":"f99a795c.3e73c8","name":"previous","props":[{"p":"command","v":"previous","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":140,"y":120,"wires":[["ad17b0b6.40d5c"]]},{"id":"56f6521d.3c5a5c","type":"inject","z":"f99a795c.3e73c8","name":"set scene 3","props":[{"p":"command","v":"set","vt":"str"},{"p":"scene","v":"Scene 3","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":130,"y":200,"wires":[["ad17b0b6.40d5c"]]},{"id":"8b2d9945.0a7068","type":"inject","z":"f99a795c.3e73c8","name":"set 0","props":[{"p":"command","v":"set","vt":"str"},{"p":"scene","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":160,"wires":[["ad17b0b6.40d5c"]]},{"id":"cc1d9423.c045c8","type":"scene-in","z":"f99a795c.3e73c8","name":"","scene":"Scene 1","active":true,"x":560,"y":80,"wires":[["2746ea7f.991456"]]},{"id":"b88b51ff.be731","type":"scene-in","z":"f99a795c.3e73c8","name":"","scene":"Scene 2","active":true,"x":560,"y":120,"wires":[["2746ea7f.991456"]]},{"id":"c0b6f568.7f5ea8","type":"scene-in","z":"f99a795c.3e73c8","name":"","scene":"Scene 3","active":false,"x":560,"y":160,"wires":[["2746ea7f.991456"]]},{"id":"b9ff1d83.cb0b5","type":"scene-in","z":"f99a795c.3e73c8","name":"","scene":"Scene 4","active":true,"x":560,"y":200,"wires":[["2746ea7f.991456"]]},{"id":"4e1e206f.61c9f","type":"scene-in","z":"f99a795c.3e73c8","name":"Scene 5 (unused)","scene":"Scene 5","active":true,"x":590,"y":240,"wires":[["2746ea7f.991456"]]},{"id":"ad17b0b6.40d5c","type":"scene-selector","z":"f99a795c.3e73c8","name":"","scenes":["Scene 1","Scene 2","Scene 3","Scene 4"],"wrapAround":true,"changedOutputOnly":false,"x":340,"y":140,"wires":[]}]
--------------------------------------------------------------------------------
/examples/shelly-25/shelly25-listen-to-input.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "ddc73c09685b6ca2",
4 | "type": "debug",
5 | "z": "d6c6189e516c5861",
6 | "name": "Log shelly input",
7 | "active": true,
8 | "tosidebar": true,
9 | "console": false,
10 | "tostatus": false,
11 | "complete": "true",
12 | "targetType": "full",
13 | "statusVal": "",
14 | "statusType": "auto",
15 | "x": 440,
16 | "y": 200,
17 | "wires": []
18 | },
19 | {
20 | "id": "876f6827a210a969",
21 | "type": "shelly-25",
22 | "z": "d6c6189e516c5861",
23 | "name": "Shelly 1 Terrace Light",
24 | "mqtt": "b1503ab27dfa3ccd",
25 | "shelly": "daf62c0c2a5c1ebd",
26 | "enableInput": false,
27 | "state": "on",
28 | "channel": "2",
29 | "inputs": 0,
30 | "outputs": 2,
31 | "customPayload": true,
32 | "payloadInput0": "",
33 | "typeInput0": "str",
34 | "payloadInput1": "",
35 | "typeInput1": "str",
36 | "x": 200,
37 | "y": 180,
38 | "wires": [
39 | [
40 | "2212d8cf185ffa8e"
41 | ],
42 | [
43 | "ddc73c09685b6ca2"
44 | ]
45 | ]
46 | },
47 | {
48 | "id": "2212d8cf185ffa8e",
49 | "type": "debug",
50 | "z": "d6c6189e516c5861",
51 | "name": "Log shelly relay",
52 | "active": true,
53 | "tosidebar": true,
54 | "console": false,
55 | "tostatus": false,
56 | "complete": "true",
57 | "targetType": "full",
58 | "statusVal": "",
59 | "statusType": "auto",
60 | "x": 440,
61 | "y": 160,
62 | "wires": []
63 | },
64 | {
65 | "id": "b1503ab27dfa3ccd",
66 | "type": "mqtt-broker",
67 | "name": "Mosquitto",
68 | "broker": "127.0.0.1",
69 | "port": "1883",
70 | "clientid": "",
71 | "autoConnect": true,
72 | "usetls": false,
73 | "protocolVersion": "4",
74 | "keepalive": "60",
75 | "cleansession": true,
76 | "birthTopic": "",
77 | "birthQos": "0",
78 | "birthPayload": "",
79 | "birthMsg": {},
80 | "closeTopic": "",
81 | "closeQos": "0",
82 | "closePayload": "",
83 | "closeMsg": {},
84 | "willTopic": "",
85 | "willQos": "0",
86 | "willPayload": "",
87 | "willMsg": {},
88 | "sessionExpiry": "",
89 | "credentials": {}
90 | },
91 | {
92 | "id": "daf62c0c2a5c1ebd",
93 | "type": "shelly-config",
94 | "name": "terrace-light",
95 | "prefix": "shellies/terrace-light"
96 | }
97 | ]
--------------------------------------------------------------------------------
/examples/shelly-25/shelly25-toggle-relay.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "8bcc1c584da5c6f6",
4 | "type": "shelly-25",
5 | "z": "d6c6189e516c5861",
6 | "name": "Shelly 1 Terrace Light",
7 | "mqtt": "b1503ab27dfa3ccd",
8 | "shelly": "daf62c0c2a5c1ebd",
9 | "enableInput": true,
10 | "state": "toggle",
11 | "channel": "0",
12 | "inputs": 1,
13 | "outputs": 3,
14 | "customPayload": false,
15 | "payloadInput0": "",
16 | "typeInput0": "str",
17 | "payloadInput1": "",
18 | "typeInput1": "str",
19 | "x": 440,
20 | "y": 400,
21 | "wires": [
22 | [],
23 | [],
24 | [
25 | "0e5a9766c04a1a6a"
26 | ]
27 | ]
28 | },
29 | {
30 | "id": "2b95eb1c49dc7662",
31 | "type": "inject",
32 | "z": "d6c6189e516c5861",
33 | "name": "Virtual light switch",
34 | "props": [
35 | {
36 | "p": "payload"
37 | },
38 | {
39 | "p": "topic",
40 | "vt": "str"
41 | }
42 | ],
43 | "repeat": "",
44 | "crontab": "",
45 | "once": false,
46 | "onceDelay": 0.1,
47 | "topic": "",
48 | "payload": "",
49 | "payloadType": "date",
50 | "x": 170,
51 | "y": 400,
52 | "wires": [
53 | [
54 | "8bcc1c584da5c6f6"
55 | ]
56 | ]
57 | },
58 | {
59 | "id": "0e5a9766c04a1a6a",
60 | "type": "send-messages",
61 | "z": "d6c6189e516c5861",
62 | "name": "",
63 | "bridge": "11358734e0ff49d5",
64 | "x": 660,
65 | "y": 400,
66 | "wires": []
67 | },
68 | {
69 | "id": "b1503ab27dfa3ccd",
70 | "type": "mqtt-broker",
71 | "name": "Mosquitto",
72 | "broker": "127.0.0.1",
73 | "port": "1883",
74 | "clientid": "",
75 | "autoConnect": true,
76 | "usetls": false,
77 | "protocolVersion": "4",
78 | "keepalive": "60",
79 | "cleansession": true,
80 | "birthTopic": "",
81 | "birthQos": "0",
82 | "birthPayload": "",
83 | "birthMsg": {},
84 | "closeTopic": "",
85 | "closeQos": "0",
86 | "closePayload": "",
87 | "closeMsg": {},
88 | "willTopic": "",
89 | "willQos": "0",
90 | "willPayload": "",
91 | "willMsg": {},
92 | "sessionExpiry": ""
93 | },
94 | {
95 | "id": "daf62c0c2a5c1ebd",
96 | "type": "shelly-config",
97 | "name": "terrace-light",
98 | "prefix": "shellies/terrace-light"
99 | },
100 | {
101 | "id": "11358734e0ff49d5",
102 | "type": "zigbee2mqtt-bridge-config",
103 | "name": "Mosquitto",
104 | "broker": "b1503ab27dfa3ccd",
105 | "baseTopic": "zigbee2mqtt",
106 | "enabledLogging": false,
107 | "allowDeviceStatusRefresh": true
108 | }
109 | ]
--------------------------------------------------------------------------------
/install.ps1:
--------------------------------------------------------------------------------
1 | # Install script to setup the project in a Test Node-RED docker container for development.
2 | # Tested under Windows and Linux (Ubuntu 20.04 with powershell)
3 | # Required software: powsershell, docker, docker-compose, nodejs, npm
4 |
5 | function copy-files {
6 | PARAM (
7 | $src,
8 | $destRoot,
9 | $destFolder
10 | )
11 |
12 | $src = Resolve-Path $src
13 | $destRoot = Resolve-Path $destRoot
14 |
15 | Write-Output "From : $src"
16 | Write-Output "To : $destRoot"
17 |
18 | $files = Get-ChildItem -Recurse $src | Where-Object { ! $_.PSIsContainer }
19 | foreach ($file in $files) {
20 | $relativeFile = $file.FullName.Substring($src.Path.Length)
21 | $dest = Join-Path -Path $destRoot -ChildPath $destFolder
22 | $dest = Join-Path -Path $dest -ChildPath $relativeFile
23 |
24 | $folder = $dest.Substring(0, $dest.LastIndexOf([IO.Path]::DirectorySeparatorChar))
25 | if (-not (Test-Path $folder)) {
26 | New-Item -ItemType Directory $folder
27 | }
28 |
29 | Write-Output $dest
30 | [System.IO.File]::Copy($file.FullName, $dest, $true);
31 | }
32 | }
33 |
34 | if (-not (Test-Path ".\debug\")) {
35 | New-Item -ItemType Directory ".\debug"
36 | }
37 |
38 | if (-not (Test-Path ".\debug\package.json")) {
39 | $check = docker container inspect -f '{{.State.Running}}' "nodered_testing"
40 |
41 | if ($check) {
42 | docker-compose down
43 | }
44 |
45 | docker-compose up -d
46 |
47 | Write-Host "Waiting for node-red to boot ..." -NoNewLine
48 | while (!(Test-Path ".\debug\package.json")) {
49 | Start-Sleep 2
50 | }
51 |
52 | Write-Host -ForegroundColor Green " done"
53 | }
54 |
55 | docker-compose down
56 |
57 | $origin = [Environment]::CurrentDirectory;
58 | $repoName = $origin.Split([IO.Path]::DirectorySeparatorChar) | Select-Object -Last 1
59 | Write-Host "Name: $repoName"
60 | if (-not (Test-Path ".\debug\myModules\$repoName")) {
61 | Write-Output "Creating Path..."
62 | New-Item -ItemType Directory ".\debug\myModules\$repoName"
63 | New-Item -ItemType Directory ".\debug\myModules\$repoName\lib"
64 | }
65 |
66 | Write-Output "Copy files..."
67 |
68 | copy-files -src '.\dist' -destRoot ".\debug\myModules\$repoName" -destFolder "dist"
69 | copy-files -src '.\examples' -destRoot ".\debug\myModules\$repoName" -destFolder "examples"
70 |
71 | $packageModified = $false
72 | $destRoot = Resolve-Path ".\debug\myModules\$repoName"
73 | # Check if package.json needs install
74 | if (Test-Path $destRoot/package.json) {
75 | Write-Output "Comparing package.json ..."
76 | $packageModified = (Get-FileHash ./package.json).hash -ne (Get-FileHash $destRoot/package.json).hash
77 | if ($packageModified) {
78 | Write-Host "package.json was modified. Reinstall again after copy!" -ForegroundColor Yellow
79 | }
80 | else {
81 | Write-Host "Unmodified. No reinstallation needed!" -ForegroundColor Green
82 | }
83 | }
84 | else {
85 | $packageModified = $true;
86 | }
87 |
88 | Copy-Item -Path ./package.json -Destination $destRoot/package.json
89 |
90 |
91 | $destination = Resolve-Path .\debug\
92 | Write-Output "Change location to: $destination"
93 | Set-Location $destination
94 |
95 | if ($packageModified) {
96 | npm install --production ".\myModules\$repoName"
97 | }
98 |
99 | Write-Output "Switching back to: $origin"
100 | Set-Location $origin
101 |
102 | # fix for windwos docker
103 | # windows docker can't handle links so we need to copy the files....
104 | if ($IsWindows -or $Env:OS -eq "Windows_NT") {
105 | Write-Host "Fixing Windows Docker Problems" -ForegroundColor Red
106 | Write-Host "Resolve symlink" -ForegroundColor Yellow
107 | $files = Get-ChildItem "./debug/node_modules/" | Where-Object { $_.Attributes -match "ReparsePoint" }
108 |
109 | Write-Host "Updating modules..." -ForegroundColor Yellow
110 | $files = Get-ChildItem "./debug/node_modules/" -Directory
111 | foreach ($file in $files) {
112 |
113 | $moduleName = $file.FullName.Split("\") | Select-Object -Last 1
114 |
115 | if (Test-Path "./debug/myModules/$moduleName") {
116 | Write-Host "Update module: " -NoNewline -ForegroundColor Magenta
117 | Write-Host $moduleName
118 | Remove-Item $file.FullName -Force -Recurse
119 | Copy-Item -Recurse -Path "./debug/myModules/$moduleName/" -Destination "./debug/node_modules/"
120 | }
121 |
122 | }
123 | }
124 |
125 | docker-compose up -d
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-zigbee2mqtt-devices",
3 | "version": "0.20.0",
4 | "description": "Nodes to interact with zigbee2mqtt for Node-RED",
5 | "author": "Christian Dirnhofer",
6 | "license": "MIT",
7 | "keywords": [
8 | "node-red",
9 | "zigbee2mqtt",
10 | "smart home"
11 | ],
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/Dirnei/node-red-contrib-zigbee2mqtt-devices"
15 | },
16 | "node-red": {
17 | "version": ">=1.2.2",
18 | "nodes": {
19 | "api": "dist/_api.js",
20 | "brdige-log": "dist/bridge-log.js",
21 | "config": "dist/zigbee2mqtt-config.js",
22 | "devices-eurotronics": "dist/nodes/devices-eurotronic.js",
23 | "devices-hue": "dist/nodes/hue-dimmer/devices-hue.js",
24 | "devices-ikea": "dist/nodes/devices-ikea.js",
25 | "devices-scenic": "dist/nodes/devices-scenic.js",
26 | "devices-sonoff": "dist/nodes/devices-sonoff.js",
27 | "devices-tasmota": "dist/nodes/devices-tasmota.js",
28 | "devices-tint": "dist/nodes/devices-tint.js",
29 | "generic-lamp": "dist/zigbee2mqtt.js",
30 | "non-z2m-devices-shelly-25": "dist/non-z2m-nodes/devices-shelly-25.js",
31 | "ota": "dist/ota.js",
32 | "override": "dist/nodes/override/override-nodes.js",
33 | "scenes": "dist/nodes/scenes.js",
34 | "sensors": "dist/nodes/sensors.js"
35 | }
36 | },
37 | "dependencies": {
38 | "mqtt": "^4.3.4",
39 | "node-red-ext-bavaria-black": "^0.5.0"
40 | },
41 | "scripts": {
42 | "build": "run-script-os",
43 | "build:nix": "tsc && rsync -rv --exclude='*.js' --exclude='*.ts' ./src/ ./dist/",
44 | "build:win32": "tsc && @powershell -NoProfile -Command ./robocopy.ps1",
45 | "builddocker": "run-script-os",
46 | "builddocker:nix": "npm run build:nix && pwsh ./install.ps1",
47 | "builddocker:win32": "npm run build:win32 && @powershell -NoProfile -Command ./install.ps1",
48 | "createnode": "run-script-os",
49 | "createnode:nix": "pwsh ./createNode.ps1",
50 | "createnode:win32": "@powershell -NoProfile -Command ./createNode.ps1",
51 | "lint": "eslint ./src/**/*.js ./test/**/*.js ./test-integration/**/*.js",
52 | "test": "mocha \"test/**/*_spec.js\"",
53 | "testintegration": "docker-compose -f test-integration/docker/docker-compose.yml up -d && mocha \"test-integration/**/*_spec.js\""
54 | },
55 | "devDependencies": {
56 | "@types/jquery": "^3.5.13",
57 | "@types/node": "^14.18.10",
58 | "@types/node-red": "^1.2.0",
59 | "eslint": "^8.8.0",
60 | "mocha": "^10.1.0",
61 | "node-red": "^3.0.2",
62 | "node-red-node-test-helper": "^0.3.0",
63 | "run-script-os": "^1.1.6",
64 | "typescript": "^4.5.5",
65 | "zigbee-herdsman": "^0.14.10"
66 | },
67 | "engines": {
68 | "node": ">=12.0.0"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/robocopy.ps1:
--------------------------------------------------------------------------------
1 | robocopy.exe ./src/ ./dist/ /MIR /xf *.js /xf *.ts /NFL /NDL /NJH /NJS /nc /ns /np
2 |
3 | Write-Output $LastExitCode
4 | if ($LastExitCode -ge 8) {
5 | Write-Output "Error copying files"
6 | exit $LastExitCode
7 | } Else {
8 | Write-Output "Copied src/ to dist/"
9 | exit 0
10 | }
--------------------------------------------------------------------------------
/src/_api.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const utils = require("./lib/utils.js");
3 |
4 | RED.httpAdmin.get("/z2m/devices/:broker/:deviceType/:vendor/:model", function (req, res) {
5 | try {
6 | var response = {
7 | success: false,
8 | message: "",
9 | devices: []
10 | };
11 |
12 | var broker = RED.nodes.getNode(req.params.broker.replace("_", "."));
13 |
14 | if (broker === undefined || broker === null) {
15 | response.message = "Unable to find broker. Please deploy first and try it again.";
16 | res.end(JSON.stringify(response));
17 | return;
18 | }
19 |
20 | var devices = broker.getDeviceList();
21 | var type = req.params.deviceType.toLowerCase();
22 |
23 | var vendor = decodeURI(req.params.vendor).toLowerCase();
24 | var model = decodeURI(req.params.model).toLowerCase();
25 |
26 | if (model !== "all" && model.includes(",")) {
27 | model = model.split(",");
28 | }
29 | else if (model !== "all") {
30 | model = [model];
31 | }
32 |
33 | response.devices = minimizeDeviceList(filterDevices(devices, type, vendor, model));
34 | response.success = devices.length > 0;
35 |
36 | if (!response.success) {
37 | response.message = "No devices found!";
38 | }
39 |
40 | if (response.success && response.devices.length == 0) {
41 | response.success = false;
42 | response.message = `No devices found after filtering! Used Filter: (Vendor: ${vendor}, Model: ${model}, Type: ${type})`;
43 | }
44 |
45 | console.log("---------------- RESPONSE -----------------");
46 | console.log(response);
47 | console.log("-------------------------------------------");
48 |
49 | res.end(JSON.stringify(response));
50 | } catch (err) {
51 | res.end(JSON.stringify(response));
52 | console.log(err);
53 | }
54 | });
55 |
56 | function minimizeDeviceList(devices) {
57 | return devices.map((value) => {
58 | return {
59 | friendly_name: value.friendly_name,
60 | address: value.ieee_address,
61 | type: value.type,
62 | model: value.definition.model,
63 | vendor: value.definition.vendor,
64 | version: value.software_build_id
65 | };
66 | });
67 | }
68 |
69 | function isClimateSensorProperty(expose) {
70 | switch (expose.property) {
71 | case "temperature":
72 | case "humidity":
73 | case "pressure":
74 | return true;
75 | }
76 |
77 | return false;
78 | }
79 |
80 | function isContactSensorProperty(expose) {
81 | return expose.property === "contact";
82 | }
83 |
84 | function isOccupancySensorProperty(expose) {
85 | return expose.property === "occupancy";
86 | }
87 |
88 | function filterDevices(devices, type, vendor, model) {
89 | var specialTypes = ["climate", "contact", "occupancy"];
90 | var isSpecialType = specialTypes.includes(type);
91 |
92 | return devices.filter(e => {
93 | try {
94 |
95 | if (e.definition === undefined || e.definition === null) {
96 | return false;
97 | }
98 |
99 | var dt = e.type.toLowerCase();
100 | var dv = "all";
101 | var dm = "all";
102 |
103 | if (isSpecialType === true && dt == "enddevice" && e.definition.exposes !== undefined) {
104 | var match = 0;
105 | e.definition.exposes.forEach(expose => {
106 | if (match === 0 && expose.property !== undefined) {
107 | switch (type) {
108 | case "climate":
109 | match |= isClimateSensorProperty(expose);
110 | break;
111 | case "contact":
112 | match |= isContactSensorProperty(expose);
113 | break;
114 | case "occupancy":
115 | match |= isOccupancySensorProperty(expose);
116 | break;
117 | }
118 | }
119 | });
120 |
121 | if (match === 1) {
122 | return true;
123 | }
124 | }
125 |
126 | if (e.definition.vendor) {
127 | dv = e.definition.vendor.toLowerCase();
128 | }
129 |
130 | if (e.definition.model) {
131 | dm = e.definition.model.toLowerCase();
132 | }
133 |
134 | return (dt == type || (type == "enddevice" && dt == "greenpower") || (type == "all" && dt !== "coordinator")) &&
135 | (dv == vendor || (vendor == "all")) &&
136 | ((model == "all") || model.includes(dm));
137 | } catch (err) {
138 | console.log(err);
139 | console.log(e);
140 | }
141 | });
142 | }
143 |
144 | RED.httpAdmin.get("/z2m/scenes", function (req, res) {
145 | try {
146 | var scenes = [];
147 | RED.nodes.eachNode(n => {
148 | if (n.type === "scene-in") {
149 | if (scenes.every(s => s != n.scene)) {
150 | scenes.push(n.scene);
151 | }
152 | }
153 | });
154 |
155 | res.end(JSON.stringify({
156 | scenes: scenes
157 | }));
158 | } catch (err) {
159 | console.log(err);
160 | }
161 | });
162 | };
163 |
--------------------------------------------------------------------------------
/src/bridge-log.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | // @ts-ignore
3 | const OutputHandler = require("./lib/outputHandler.js");
4 | const utils = require("./lib/utils.js");
5 | const bavaria = utils.bavaria();
6 |
7 | /**
8 | * The bridge log provides an easy way to filter logs that are
9 | * published into the zigbee2mqtt/bridge/log MQTT topic.
10 | * You configure the types you are interested in,
11 | * and the node creates one output for each type.
12 | * @param {*} config for the node
13 | */
14 | function bridgeLog(config) {
15 | RED.nodes.createNode(this, config);
16 | const bridgeNode = RED.nodes.getNode(config.bridge);
17 | const node = this;
18 | const outputHandler = new OutputHandler();
19 |
20 | utils.setConnectionState(bridgeNode, node);
21 |
22 | // Create a list of log message types to listen to
23 | const enabledLogTypes = new Array();
24 | let outputCount = 0;
25 |
26 | Object.keys(config).forEach(function(key) {
27 | if(key.startsWith("type_") && config[key]) {
28 | const logType = key.replace("type_", "");
29 | enabledLogTypes.push(logType);
30 | outputHandler.addOutput(outputCount, logType, logType, "Logs with the type: " + logType, msg => { return msg.message; });
31 | outputCount++;
32 | }
33 | });
34 |
35 | bridgeNode.on("bridge-log", (message) => {
36 |
37 | // Check if the log type is configured
38 | if (enabledLogTypes.indexOf(message.type) >= 0) {
39 | let messageForNOutputs = outputHandler.prepareOutput("type", message);
40 |
41 | if(config.consolidate_output) {
42 | // If all messages should be consolidated into one output,
43 | // use the last element of the array.
44 | // Because the output handler sets all the other indexes to null,
45 | // nothing is sent to those outputs.
46 | messageForNOutputs = messageForNOutputs.slice(-1);
47 | }
48 |
49 | node.send(messageForNOutputs);
50 | }
51 | });
52 |
53 | // Set the node status
54 | bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
55 | node.status({ fill: "green", text: "connected" });
56 | });
57 | }
58 | RED.nodes.registerType("bridge-log", bridgeLog);
59 | };
--------------------------------------------------------------------------------
/src/device-types.ts:
--------------------------------------------------------------------------------
1 | export type Z2mDeviceEntry = {
2 | date_code: number,
3 | friendly_name: string,
4 | ieee_address: string,
5 | interview_completed: boolean,
6 | interviewing: boolean,
7 | model_id: string,
8 | network_address: number,
9 | power_source: string,
10 | supported: boolean,
11 | type: string,
12 | definition: Z2mDeviceDefinition
13 | }
14 |
15 | export type Z2mDeviceDefinition = {
16 | description: string,
17 | model: string,
18 | vendor: string,
19 | exposes: Array
20 | }
21 |
22 | export type Z2mDeviceExposesBase = {
23 | type: string
24 | }
25 |
26 | export type Z2mDeviceExposesFeatures = Z2mDeviceExposesBase & {
27 | features: Array
28 | }
29 |
30 | export type Z2mDeviceProperty = Z2mDeviceExposesBase & {
31 | access: number,
32 | description: string,
33 | name: string,
34 | property: string,
35 | unit: string
36 | }
37 |
38 | export type Z2mDeviceSwitchableProperty = Z2mDeviceProperty & {
39 | value_on: string,
40 | value_off: string,
41 | value_toggle: string,
42 | }
43 |
44 | export type Z2mDeviceListProperty = Z2mDeviceProperty & {
45 | values: Array,
46 | }
47 |
48 | export type Z2mDeviceRangeProperty = Z2mDeviceProperty & {
49 | value_min: number,
50 | value_max: number,
51 | }
52 |
53 | export type Z2mDeviceStepableProperty = Z2mDeviceRangeProperty & {
54 | value_step: number,
55 | }
--------------------------------------------------------------------------------
/src/icons/remote-black.svg:
--------------------------------------------------------------------------------
1 |
2 |
87 |
--------------------------------------------------------------------------------
/src/icons/remote.svg:
--------------------------------------------------------------------------------
1 |
2 |
87 |
--------------------------------------------------------------------------------
/src/lib/mqtt.ts:
--------------------------------------------------------------------------------
1 | // import { log } from "node-red";
2 |
3 | export type MqttSubscriptionCallback = (payload: any, topic: string) => void
4 |
5 | export class MqttSubscription {
6 | topic: string;
7 | regex: RegExp;
8 | jsonPayload: boolean;
9 | callback: MqttSubscriptionCallback;
10 |
11 | constructor(topic: string, jsonPayload: boolean, callback: MqttSubscriptionCallback){
12 | this.topic = topic;
13 |
14 | // create regex for matching mqtt subscriptions containing whitespace chars # and +
15 | let tmp: string = topic.split("+").join("[^\/]*");
16 | tmp = tmp.split("#").join(".+");
17 | this.regex = new RegExp(tmp + "$");
18 | this.callback = callback;
19 | this.jsonPayload = jsonPayload;
20 | }
21 |
22 | invokeIfMatch(topic: string, payload: any) : void {
23 | if (this.comapreTopic(topic)) {
24 | if (this.jsonPayload) {
25 | try{
26 | payload = JSON.parse(payload);
27 | } catch(err){
28 | // log.error("################################################################");
29 | // log.error(" node-red-contrib-zigbee2mqtt-devices error:");
30 | // log.error(err);
31 | // log.error("################################################################");
32 | }
33 | }
34 |
35 | this.callback(payload, topic);
36 | }
37 | }
38 |
39 | comapreTopic(topic: string) : boolean {
40 | return this.regex.test(topic);
41 | }
42 | }
--------------------------------------------------------------------------------
/src/lib/outputHandler.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creates dynamic outputs for nodes and sends messages to the desired output.
3 | */
4 | module.exports = class OutputHandler {
5 |
6 | /**
7 | * Creates a new dynamic output for the node.
8 | * @param {outputIndex}: Fixed index of the output.
9 | * @param {outputLabel}: The label of the output that is shown when hovering over it with the mouse. This value will be stored in msg.action.name to trace form which output the message came from.
10 | * @param {compareValue}: A unique identifier that is used in the prepareOutput method to send messages to this output. Prepare output checks that a property in the message object matches the actionName.
11 | * @param {actionDescription}: Human readable description stored in: message.action.description
12 | * @param {payload}: Fixed payload or function where to get the payload from e.g. "test" or msg => { msg.payload.valueX; }.
13 | */
14 | addOutput(outputIndex, outputLabel, compareValue, actionDescription, payload) {
15 |
16 | if (!this.ioMap) {
17 | this.ioMap = {};
18 | }
19 |
20 | this.ioMap[compareValue] = {
21 | index: outputIndex,
22 | name: outputLabel,
23 | description: actionDescription,
24 | payload: payload
25 | };
26 |
27 | return this;
28 | }
29 |
30 | /**
31 | * Checks if the property specified in actionNamePathFilter matches the compareValue of one of the outputs. Then it sends the message to the matching output.
32 | * If actionNamePathFilter is set to "myvalue", this function checks that one of the outputs compareValues has the same value as message.myvalue.
33 | * @param {actionNamePathFilter}: Path to property to compare with compareValue.
34 | */
35 | prepareOutput(actionNamePathFilter, msg) {
36 | var path = actionNamePathFilter.split(".");
37 | var value = msg;
38 | var isExtendedMessage = false;
39 | path.forEach(property => {
40 | value = value[property];
41 | });
42 |
43 | var data = this.ioMap[value];
44 | var payload = data.payload;
45 |
46 | if (typeof data.payload === "function") {
47 | payload = data.payload(msg);
48 | }
49 |
50 | if (payload && Object.prototype.hasOwnProperty.call(payload, "payload") === true) {
51 | isExtendedMessage = true;
52 | }
53 |
54 | var output = [];
55 | for (var i = 0; i < data.index; i++) {
56 | output.push(null);
57 | }
58 |
59 | var action = {
60 | name: data.name,
61 | description: data.description,
62 | };
63 |
64 | if (isExtendedMessage) {
65 | payload = payload.payload;
66 | }
67 |
68 | var preparedMessage = {
69 | action: action,
70 | payload: Object.assign({}, payload),
71 | };
72 |
73 | output.push(preparedMessage);
74 | return output;
75 | }
76 | };
--------------------------------------------------------------------------------
/src/lib/utils.js:
--------------------------------------------------------------------------------
1 |
2 | function setConnectionState(bridgeNode, node) {
3 | if (bridgeNode.isConnected() === true) {
4 | node.status({ fill: "green", text: "connected" });
5 | } else {
6 | node.status({ fill: "blue", text: "not connected" });
7 | }
8 | }
9 |
10 | function createButtonOutput(output, name, type, payload) {
11 | return {
12 | index: output,
13 | button_name: name,
14 | button_type: type,
15 | button_payload: payload,
16 | };
17 | }
18 |
19 | function sendAt(node, index, msg) {
20 | var output = [];
21 | for (var i = 0; i < index; i++) {
22 | output.push(null);
23 | }
24 |
25 | output.push(msg);
26 | node.send(output);
27 | }
28 | function createAction(property, type, value) {
29 | return {
30 | payload: {
31 | override: {
32 | action: {
33 | name: `${property}_${type}`,
34 | value: value
35 | }
36 | }
37 | }
38 | };
39 | }
40 |
41 | function createSceneCommand(command, value) {
42 | return {
43 | command: command,
44 | scene: value
45 | };
46 | }
47 |
48 | function createStateOverride(value) {
49 | var convertedValue = convertToOnOff(value);
50 | var override = createOverride("state", convertedValue);
51 | if(convertedValue === "ON"){
52 | override.brightness = 254;
53 | }
54 |
55 | return override;
56 | }
57 |
58 | function createOverride(name, value) {
59 | var override = {};
60 | override[name] = value;
61 | return override;
62 | }
63 |
64 | function convertToOnOff(value) {
65 | switch (value) {
66 | case "on":
67 | case "ON":
68 | case 1:
69 | case true:
70 | return "ON";
71 | case "off":
72 | case "OFF":
73 | case 0:
74 | case false:
75 | return "OFF";
76 | }
77 | }
78 |
79 | function propertyExists(obj, name)
80 | {
81 | return obj !== undefined && Object.prototype.hasOwnProperty.call(obj, name);
82 | }
83 |
84 | const bavaria = require("node-red-ext-bavaria-black");
85 |
86 | module.exports = {
87 | createButtonOutput: createButtonOutput,
88 | bavaria: () => bavaria,
89 | sendAt: sendAt,
90 | propertyExists: propertyExists,
91 | setConnectionState: setConnectionState,
92 | payloads: {
93 | createColorTempStep: (value) => { return createAction("color_temp", "step", value); },
94 | createColorTempMove: (value) => { return createAction("color_temp", "move", value == 0 ? "stop" : value); },
95 |
96 | createBrightnessStep: (value) => { return createAction("brightness", "step_onoff", value); },
97 | createBrightnessMove: (value) => { return createAction("brightness", "move_onoff", value); },
98 |
99 | createHueStep: (value) => { return createAction("hue", "step", value); },
100 | createHueMove: (value) => { return createAction("hue", "move", value); },
101 |
102 | createSaturationStep: (value) => { return createAction("saturation", "step", value); },
103 | createSaturationMove: (value) => { return createAction("saturation", "move", value); },
104 | createNextSceneCommand: () => { return createSceneCommand("next", undefined); },
105 | createPreviousSceneCommand: () => { return createSceneCommand("previous", undefined); },
106 | createSetSceneCommand: (value) => { return createSceneCommand("set", value); },
107 | convertToOnOff: convertToOnOff,
108 | overrides: {
109 | createStateOverride: (value) => { return { override: createStateOverride(value) }; },
110 | createBrightnessOverride: (value) => { return { override: createOverride("brightness", value) }; },
111 | },
112 | devices: {
113 | addDevice: (msg, device) => {
114 | if (msg.payload === undefined || typeof msg.payload != "object") {
115 | msg.payload = {};
116 | }
117 |
118 | if (msg.payload.devices === undefined) {
119 | msg.payload.devices = [];
120 | }
121 |
122 | msg.payload.devices.push(device);
123 | return msg;
124 | }
125 | }
126 | },
127 | outputs: {
128 | preapreOutputFor: (index, payload) => {
129 | var output = [];
130 | for (var i = 0; i < index; i++) {
131 | output.push(null);
132 | }
133 |
134 | output.push(payload);
135 | return output;
136 | }
137 | },
138 | ui: {
139 | input: {
140 | getPayload: function (data, type) {
141 | switch (type) {
142 | case "num":
143 | return Number.parseFloat(data);
144 | case "bool":
145 | return data == true;
146 | case "json":
147 | return JSON.parse(data);
148 | }
149 |
150 | return data;
151 | }
152 | }
153 | }
154 | };
--------------------------------------------------------------------------------
/src/nodes/devices-eurotronic.html:
--------------------------------------------------------------------------------
1 |
2 |
49 |
50 |
79 |
80 |
--------------------------------------------------------------------------------
/src/nodes/devices-eurotronic.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const utils = require("../lib/utils.js");
3 | const bavaria = utils.bavaria();
4 |
5 | function createEurotronicsSpirit(config) {
6 | RED.nodes.createNode(this, config);
7 | var node = this;
8 | var bridgeNode = RED.nodes.getNode(config.bridge);
9 |
10 | if (config.windowSensor !== undefined) {
11 | bridgeNode.subscribeDevice(node.id, config.windowSensor, function (msg) {
12 | bridgeNode.publishDevice(config.deviceName, {
13 | eurotronic_host_flags: {
14 | window_open: !msg.contact,
15 | }
16 | });
17 | });
18 | }
19 |
20 | bridgeNode.subscribeDevice(node.id + "1", config.deviceName, function (msg) {
21 | var text = `Battery: ${msg.battery}% `;
22 | text += `T: ${msg.local_temperature}° `;
23 | text += `SP: ${msg.current_heating_setpoint}°`;
24 | node.status({ fill: "green", text: text });
25 | });
26 |
27 | function containsProperty(msg, name, type) {
28 | return msg.payload[name] !== undefined && typeof msg.payload[name] == type;
29 | }
30 |
31 | utils.setConnectionState(bridgeNode, node);
32 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
33 | node.status({ fill: "green", text: "connected" });
34 | });
35 |
36 | node.on("input", function (msg) {
37 | if (containsProperty(msg, "heatingSetpoint", "number")
38 | || containsProperty(msg, "child_protection", "boolean")
39 | || containsProperty(msg, "mirror_display", "boolean")) {
40 | if (msg.payload.devices === undefined) {
41 | msg.payload.devices = [];
42 | }
43 |
44 | var device = {
45 | topic: config.deviceName,
46 | current_heating_setpoint: msg.payload.heatingSetpoint,
47 | eurotronic_host_flags: {
48 | child_protection: msg.payload.child_protection !== undefined ? msg.payload.child_protection : config.childProtection === true,
49 | mirror_display: msg.payload.mirror_display !== undefined ? msg.payload.mirror_display : config.mirrorDisplay === true,
50 | },
51 | target: "z2m"
52 | };
53 |
54 | msg.payload.heatingSetpoint = undefined;
55 | msg.payload.child_protection = undefined;
56 | msg.payload.mirror_display = undefined;
57 |
58 | msg.payload.devices.push(device);
59 | node.send(msg);
60 | }
61 | });
62 |
63 | node.on("close", function () {
64 | bavaria.observer.unregister(regId);
65 | });
66 | }
67 |
68 | RED.nodes.registerType("eurotronic-spirit", createEurotronicsSpirit);
69 | };
70 |
--------------------------------------------------------------------------------
/src/nodes/devices-scenic.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
44 |
45 |
--------------------------------------------------------------------------------
/src/nodes/devices-scenic.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function (RED) {
3 | const utils = require("../lib/utils.js");
4 | const bavaria = utils.bavaria();
5 |
6 | function scenicSwitch(config) {
7 | RED.nodes.createNode(this, config);
8 | var bridgeNode = RED.nodes.getNode(config.bridge);
9 | var node = this;
10 |
11 | utils.setConnectionState(bridgeNode, node);
12 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
13 | node.status({ fill: "green", text: "connected" });
14 | bridgeNode.subscribeDevice(node.id, config.deviceName, function (message) {
15 | if (message.action === undefined || message.action === "" || message.action === null) {
16 | // Ignore message with empty action
17 | return;
18 | }
19 |
20 | var ioMap = {
21 | press_1: utils.createButtonOutput(0, "A0", "pressed"),
22 | release_1: utils.createButtonOutput(0, "A0", "released"),
23 | press_2: utils.createButtonOutput(1, "A1", "pressed"),
24 | release_2: utils.createButtonOutput(1, "A1", "released"),
25 | press_3: utils.createButtonOutput(2, "B0", "pressed"),
26 | release_3: utils.createButtonOutput(2, "B0", "released"),
27 | press_4: utils.createButtonOutput(3, "B1", "pressed"),
28 | release_4: utils.createButtonOutput(3, "B1", "released"),
29 | press_1_and_3: utils.createButtonOutput(4, "UP", "pressed"),
30 | release_1_and_3: utils.createButtonOutput(4, "UP", "released"),
31 | press_2_and_4: utils.createButtonOutput(5, "DOWN", "pressed"),
32 | release_2_and_4: utils.createButtonOutput(5, "DOWN", "released"),
33 | };
34 |
35 | var output = ioMap[message.action];
36 | utils.sendAt(node, output.index, {
37 | payload: {
38 | button_name: output.button_name,
39 | button_type: output.button_type,
40 | }
41 | });
42 | });
43 | });
44 |
45 | node.on("close", ()=>{
46 | bavaria.observer.unregister(regId);
47 | });
48 | }
49 | RED.nodes.registerType("scenic-foh-switch", scenicSwitch);
50 | };
--------------------------------------------------------------------------------
/src/nodes/devices-sonoff.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
44 |
45 |
--------------------------------------------------------------------------------
/src/nodes/devices-sonoff.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const utils = require("../lib/utils.js");
3 | const bavaria = utils.bavaria();
4 |
5 | function sonoffButton(config) {
6 | RED.nodes.createNode(this, config);
7 | var bridgeNode = RED.nodes.getNode(config.bridge);
8 | var node = this;
9 |
10 | utils.setConnectionState(bridgeNode, node);
11 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
12 | node.status({ fill: "green", text: "connected" });
13 | bridgeNode.subscribeDevice(node.id, config.deviceName, function (message) {
14 | try {
15 | if (message.action === undefined || message.action === "" || message.action === null) {
16 | // Ignore message with empty action
17 | return;
18 | }
19 |
20 | const ioMap = {
21 | single: utils.createButtonOutput(0, "button", "pressed"),
22 | long: utils.createButtonOutput(0, "button", "released"),
23 | double: utils.createButtonOutput(0, "button", "double"),
24 | };
25 |
26 | var output = ioMap[message.action];
27 | utils.sendAt(node, output.index, {
28 | payload: {
29 | button_name: output.button_name,
30 | button_type: output.button_type,
31 | }
32 | });
33 |
34 | node.status({ fill: "green", "text": "Last action: " + output.button_type });
35 | setTimeout(function () {
36 | node.status({ fill: "green", text: "connected" });
37 | }, 2000);
38 | } catch (err) {
39 | node.error(err);
40 | node.status({ fill: "red", "text": "error" });
41 | }
42 | });
43 | });
44 |
45 | node.on("close", () => {
46 | bavaria.observer.unregister(regId);
47 | });
48 | }
49 | RED.nodes.registerType("sonoff-button", sonoffButton);
50 | };
--------------------------------------------------------------------------------
/src/nodes/devices-tasmota.html:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
47 |
48 |
--------------------------------------------------------------------------------
/src/nodes/devices-tasmota.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const utils = require("../lib/utils.js");
3 | const bavaria = utils.bavaria();
4 |
5 | function createTasmota(config) {
6 | RED.nodes.createNode(this, config);
7 | var node = this;
8 | const possibleValues = ["on", "off", "toggle", "0", "1", "2"];
9 |
10 | function preparePayload(msg)
11 | {
12 | node.warn(msg);
13 | return msg;
14 | }
15 |
16 | node.on("input", function (msg) {
17 | if (possibleValues.includes(msg.payload.toString().toLowerCase()))
18 | {
19 | utils.payloads.devices.addDevice(msg, {
20 | topic: `cmnd/${config.topic}/power`,
21 | state: utils.payloads.convertToOnOff(msg.payload),
22 | target: "mqtt",
23 | payloadGenerator: preparePayload
24 | });
25 | node.send(msg);
26 | }
27 | });
28 |
29 | node.on("close", function () {
30 | });
31 | }
32 |
33 | RED.nodes.registerType("tasmota", createTasmota);
34 | };
35 |
--------------------------------------------------------------------------------
/src/nodes/devices-tint.html:
--------------------------------------------------------------------------------
1 |
2 |
40 |
41 |
86 |
87 |
--------------------------------------------------------------------------------
/src/nodes/devices-tint.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const OutputHandler = require("../lib/outputHandler.js");
3 | const utils = require("../lib/utils.js");
4 | const bavaria = utils.bavaria();
5 |
6 | function tintRemote(config) {
7 | RED.nodes.createNode(this, config);
8 | var bridgeNode = RED.nodes.getNode(config.bridge);
9 | var node = this;
10 | var nodeConext = this.context();
11 |
12 | utils.setConnectionState(bridgeNode, node);
13 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
14 | node.status({ fill: "green", text: "connected" });
15 | });
16 |
17 | function getDirection(msg) {
18 | var lastTemp = nodeConext.get("lastColorTemp");
19 | var currentTemp = msg.action_color_temperature;
20 | nodeConext.set("lastColorTemp", currentTemp);
21 |
22 | if (currentTemp === 150) {
23 | return "colder";
24 | } else if (currentTemp >= 370 && (!lastTemp || lastTemp < 370)) {
25 | return "warmer";
26 | }
27 |
28 | if (!lastTemp) {
29 | return "unknown";
30 | }
31 |
32 | return lastTemp > currentTemp ? "colder" : "warmer";
33 | }
34 |
35 | function getColorTempStep(msg) {
36 | var step = config.temperatureChange || 50;
37 | var direction = getDirection(msg);
38 | if (direction === "colder") {
39 | step *= -1;
40 | } else if (direction === "unknown") {
41 | step = 0;
42 | }
43 |
44 | return step;
45 | }
46 |
47 | var handler = new OutputHandler();
48 | handler
49 | .addOutput(0, "power", "on", "pressed", config.suppressPowerpayload ? "toggle" : "on")
50 | .addOutput(0, "power", "off", "pressed", config.suppressPowerpayload ? "toggle" : "off")
51 | .addOutput(1, "color", "color_wheel", "pressed", (msg) => {
52 | return bavaria.converter.xyToRgb(msg.action_color.x, msg.action_color.y);
53 | })
54 | .addOutput(2, "temperature", "color_temp", "pressed", (msg) => { return utils.payloads.createColorTempStep(getColorTempStep(msg)); })
55 | .addOutput(3, "brightness_up", "brightness_up_click", "pressed", utils.payloads.createBrightnessStep(config.brightnessChange))
56 | .addOutput(3, "brightness_up", "brightness_up_hold", "hold", utils.payloads.createBrightnessMove(config.brightnessChange))
57 | .addOutput(3, "brightness_up", "brightness_up_release", "released", utils.payloads.createBrightnessMove(0))
58 | .addOutput(4, "brightness_down", "brightness_down_click", "pressed", utils.payloads.createBrightnessStep(0 - config.brightnessChange))
59 | .addOutput(4, "brightness_down", "brightness_down_hold", "hold", utils.payloads.createBrightnessMove(0 - config.brightnessChange))
60 | .addOutput(4, "brightness_down", "brightness_down_release", "released", utils.payloads.createBrightnessMove(0))
61 | .addOutput(5, "scene", "scene_3", "pressed", utils.payloads.createSetSceneCommand(0))
62 | .addOutput(5, "scene", "scene_1", "pressed", utils.payloads.createSetSceneCommand(1))
63 | .addOutput(5, "scene", "scene_2", "pressed", utils.payloads.createSetSceneCommand(2))
64 | .addOutput(5, "scene", "scene_6", "pressed", utils.payloads.createSetSceneCommand(3))
65 | .addOutput(5, "scene", "scene_4", "pressed", utils.payloads.createSetSceneCommand(4))
66 | .addOutput(5, "scene", "scene_5", "pressed", utils.payloads.createSetSceneCommand(5));
67 |
68 | bridgeNode.subscribeDevice(node.id, config.deviceName, function (msg) {
69 | try {
70 | node.send(handler.prepareOutput("action", msg));
71 | node.status({ fill: "green", "text": "Last action: " + msg.action });
72 | setTimeout(function () {
73 | utils.setConnectionState(bridgeNode, node);
74 | }, 2000);
75 | } catch (err) {
76 | node.error(err);
77 | node.status({ fill: "red", "text": "error" });
78 | }
79 | });
80 |
81 | node.on("close", () => {
82 | bridgeNode.unsubscribe(node.id);
83 | bavaria.observer.unregister(regId);
84 | });
85 | }
86 | RED.nodes.registerType("tint-remote", tintRemote);
87 | };
--------------------------------------------------------------------------------
/src/nodes/hue-dimmer/devices-hue.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
30 |
46 |
47 |
65 |
--------------------------------------------------------------------------------
/src/nodes/hue-dimmer/devices-hue.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const utils = require("../../lib/utils.js");
3 | const bavaria = utils.bavaria();
4 |
5 | function hueDimmerSwitch(config) {
6 | RED.nodes.createNode(this, config);
7 | var bridgeNode = RED.nodes.getNode(config.bridge);
8 | var node = this;
9 |
10 | utils.setConnectionState(bridgeNode, node);
11 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
12 | node.status({ fill: "green", text: "connected" });
13 | bridgeNode.subscribeDevice(node.id, config.deviceName, function (message) {
14 | if (message.action === undefined || message.action === "" || message.action === null) {
15 | // Ignore message with empty action
16 | return;
17 | }
18 |
19 | message.action = message.action.split("-").join("_");
20 |
21 | const ioMap = {};
22 | ioMap["on_press"] = utils.createButtonOutput(0, "on", "pressed");
23 | ioMap["on_hold"] = utils.createButtonOutput(0, "on", "hold");
24 | ioMap["on_hold_release"] = utils.createButtonOutput(0, "on", "released");
25 | ioMap["off_press"] = utils.createButtonOutput(1, "off", "pressed");
26 | ioMap["off_hold"] = utils.createButtonOutput(1, "off", "hold");
27 | ioMap["off_hold_release"] = utils.createButtonOutput(1, "off", "released");
28 | ioMap["up_press"] = utils.createButtonOutput(2, "up", "pressed");
29 | ioMap["up_hold"] = utils.createButtonOutput(2, "up", "hold");
30 | ioMap["up_hold_release"] = utils.createButtonOutput(2, "up", "released");
31 | ioMap["down_press"] = utils.createButtonOutput(3, "down", "pressed");
32 | ioMap["down_hold"] = utils.createButtonOutput(3, "down", "hold");
33 | ioMap["down_hold_release"] = utils.createButtonOutput(3, "down", "released");
34 |
35 | var output = ioMap[message.action];
36 | utils.sendAt(node, output.index, {
37 | payload: {
38 | button_name: output.button_name,
39 | button_type: output.button_type,
40 | }
41 | });
42 | });
43 | });
44 |
45 | node.on("close", ()=>{
46 | bavaria.observer.unregister(regId);
47 | });
48 | }
49 | RED.nodes.registerType("hue-dimmer-switch", hueDimmerSwitch);
50 | };
51 |
--------------------------------------------------------------------------------
/src/nodes/override/override-nodes.ts:
--------------------------------------------------------------------------------
1 | import {NodeAPI, NodeMessageInFlow} from "node-red";
2 | import {
3 | OverrideActionNode,
4 | OverrideActionNodeDef,
5 | OverrideBrightnessNode,
6 | OverrideBrightnessNodeDef,
7 | OverrideColorNode,
8 | OverrideColorNodeDef,
9 | OverrideStateNode,
10 | OverrideStateNodeDef,
11 | OverrideTemperatureNode,
12 | OverrideTemperatureNodeDef
13 | } from "./types";
14 |
15 | module.exports = function (RED: NodeAPI) {
16 | const utils = require("../../lib/utils.js");
17 | const bavaria = utils.bavaria();
18 |
19 | function PayloadOverride(msg: T, payload: U): asserts msg is (T & { payload: { override: U } }) {
20 | Object.assign(msg, {
21 | payload: {
22 | ...msg.payload as object,
23 | override: {
24 | // @ts-ignore TODO: Weg finden welcher TypeGuard sagt was sache ist
25 | ...(typeof msg.payload === "object" && msg.payload !== null && ("override" in msg.payload) && msg.payload.override),
26 | ...payload
27 | }
28 | }
29 | });
30 | }
31 |
32 | /**
33 | *
34 | * @param config
35 | * @constructor
36 | */
37 | function OverrideStateConstructor(this: OverrideStateNode, config: OverrideStateNodeDef) {
38 | RED.nodes.createNode(this, config);
39 |
40 | this.on("input", (msg: NodeMessageInFlow) => {
41 | PayloadOverride(msg, {state: config.state});
42 | this.send(msg);
43 | });
44 | }
45 |
46 | RED.nodes.registerType("override-state", OverrideStateConstructor);
47 |
48 | /**
49 | *
50 | * @param config
51 | * @constructor
52 | */
53 | function OverrideBrightnessConstructor(this: OverrideBrightnessNode, config: OverrideBrightnessNodeDef) {
54 | RED.nodes.createNode(this, config);
55 |
56 | this.on("input", (msg) => {
57 | PayloadOverride(msg, {brightness: config.brightness});
58 | this.send(msg);
59 | });
60 | }
61 |
62 | RED.nodes.registerType("override-brightness", OverrideBrightnessConstructor);
63 |
64 | /**
65 | *
66 | * @param config
67 | * @constructor
68 | */
69 | function OverrideTemperatureConstructor(this: OverrideTemperatureNode, config: OverrideTemperatureNodeDef) {
70 | RED.nodes.createNode(this, config);
71 | this.on("input", (msg) => {
72 |
73 | PayloadOverride(msg, {temperature: config.temperature});
74 | this.send(msg);
75 | });
76 | }
77 |
78 | RED.nodes.registerType("override-temperature", OverrideTemperatureConstructor);
79 |
80 | /**
81 | *
82 | * @param config
83 | * @constructor
84 | */
85 | function OverrideColorConstructor(this: OverrideColorNode, config: OverrideColorNodeDef) {
86 | RED.nodes.createNode(this, config);
87 | this.on("input", (msg) => {
88 | PayloadOverride(msg, {
89 | color: {
90 | r: config.red,
91 | g: config.green,
92 | b: config.blue,
93 | }
94 | });
95 | this.send(msg);
96 | });
97 | }
98 |
99 | RED.nodes.registerType("override-color", OverrideColorConstructor);
100 |
101 | /**
102 | *
103 | * @param config
104 | * @constructor
105 | */
106 | function OverrideActionConstructor(this: OverrideActionNode, config: OverrideActionNodeDef) {
107 | RED.nodes.createNode(this, config);
108 |
109 | this.on("input", (msg) => {
110 |
111 | switch (config.mode) {
112 | case "brightness_move":
113 | PayloadOverride(msg, {action: utils.payloads.createBrightnessMove(config.value)});
114 | break;
115 | case "brightness_step":
116 | PayloadOverride(msg, {action: utils.payloads.createBrightnessStep(config.value)});
117 | break;
118 | case "color_temp_move":
119 | PayloadOverride(msg, {action: utils.payloads.createColorTempMove(config.value)});
120 | break;
121 | case "color_temp_step":
122 | PayloadOverride(msg, {action: utils.payloads.createColorTempStep(config.value)});
123 | break;
124 | case "hue_move":
125 | PayloadOverride(msg, {action: utils.payloads.createHueMove(config.value)});
126 | break;
127 | case "hue_step":
128 | PayloadOverride(msg, {action: utils.payloads.createHueStep(config.value)});
129 | break;
130 | case "saturation_move":
131 | PayloadOverride(msg, {action: utils.payloads.createSaturationMove(config.value)});
132 | break;
133 | case "saturation_step":
134 | PayloadOverride(msg, {action: utils.payloads.createSaturationStep(config.value)});
135 | break;
136 | }
137 | msg.payload.override = msg.payload.override.action.payload.override;
138 | this.send(msg);
139 | });
140 | }
141 |
142 | RED.nodes.registerType("override-action", OverrideActionConstructor);
143 | };
--------------------------------------------------------------------------------
/src/nodes/override/types.ts:
--------------------------------------------------------------------------------
1 | import {Node, NodeDef, NodeMessage} from "node-red";
2 |
3 |
4 | export type OverridePayload = { override: OverrideStatePayload | OverrideBrightnessPayload | OverrideTemperaturePayload | OverrideColorPayload | OverrideActionPayload }
5 |
6 | /*********
7 | * OverrideState
8 | */
9 | export interface OverrideStateOptions {
10 | state: "ON" | "OFF" | "TOGGLE" // default ON
11 | }
12 |
13 | export interface OverrideStateNodeDef extends NodeDef, OverrideStateOptions {
14 | }
15 |
16 | export interface OverrideStateNode extends Node {
17 | state: OverrideStateOptions['state']
18 |
19 | send(msg: OverrideSateMessageOut): void
20 | }
21 |
22 | export interface OverrideSateMessageOut extends NodeMessage {
23 | payload: {
24 | override: OverrideStatePayload
25 | }
26 | }
27 |
28 | export type OverrideStatePayload = {
29 | state: OverrideStateOptions['state']
30 | }
31 |
32 | /*********
33 | * OverrideState
34 | */
35 | export interface OverrideBrightnessOptions {
36 | brightness: number
37 | }
38 |
39 | export interface OverrideBrightnessNodeDef extends NodeDef, OverrideBrightnessOptions {
40 | }
41 |
42 | export interface OverrideBrightnessNode extends Node {
43 |
44 | send(msg: OverrideBrightnessMessageOut): void
45 | }
46 |
47 | export interface OverrideBrightnessMessageOut extends NodeMessage {
48 | payload: {
49 | override: OverrideBrightnessPayload
50 | }
51 | }
52 |
53 | export type OverrideBrightnessPayload = {
54 | brightness: OverrideBrightnessOptions['brightness']
55 | }
56 |
57 |
58 | /*********
59 | * OverrideTemperature
60 | */
61 | export interface OverrideTemperatureOptions {
62 | temperature: number
63 | }
64 |
65 | export interface OverrideTemperatureNodeDef extends NodeDef, OverrideTemperatureOptions {
66 | }
67 |
68 | export interface OverrideTemperatureNode extends Node {
69 |
70 | send(msg: OverrideTemperatureMessageOut): void
71 | }
72 |
73 | export interface OverrideTemperatureMessageOut extends NodeMessage {
74 | payload: {
75 | override: OverrideTemperaturePayload
76 | }
77 | }
78 |
79 | export type OverrideTemperaturePayload = {
80 | temperature: OverrideTemperatureOptions['temperature']
81 | }
82 |
83 |
84 | /*********
85 | * OverrideColor
86 | */
87 | export interface OverrideColorOptions {
88 | red: number
89 | green: number
90 | blue: number
91 | }
92 |
93 | export interface OverrideColorNodeDef extends NodeDef, OverrideColorOptions {
94 | }
95 |
96 | export interface OverrideColorNode extends Node {
97 |
98 | send(msg: OverrideColorMessageOut): void
99 | }
100 |
101 | export interface OverrideColorMessageOut extends NodeMessage {
102 | payload: {
103 | override: OverrideColorPayload
104 | }
105 | }
106 |
107 | export type OverrideColorPayload = {
108 | color: {
109 | r: OverrideColorOptions['red']
110 | g: OverrideColorOptions['green']
111 | b: OverrideColorOptions['blue']
112 | }
113 | }
114 |
115 |
116 | /*********
117 | * OverrideAction
118 | */
119 | export type ActionTypes = "brightness_move" | "brightness_step" | "color_temp_move" | "color_temp_step" | "hue_move" | "hue_step" | "saturation_move" | "saturation_step"
120 |
121 | export interface OverrideActionOptions {
122 | value: number
123 | mode: ActionTypes
124 |
125 | }
126 |
127 | export interface OverrideActionNodeDef extends NodeDef, OverrideActionOptions {
128 | }
129 |
130 | export interface OverrideActionNode extends Node {
131 |
132 | send(msg: OverrideActionMessageOut): void
133 | }
134 |
135 | export interface OverrideActionMessageOut extends NodeMessage {
136 | payload: {
137 | override: OverrideActionPayload
138 | }
139 | }
140 |
141 | export type OverrideActionPayload = {}
142 |
143 |
144 |
--------------------------------------------------------------------------------
/src/nodes/scenes.html:
--------------------------------------------------------------------------------
1 |
2 |
44 |
45 |
58 |
59 |
62 |
63 |
64 |
140 |
141 |
170 |
171 |
--------------------------------------------------------------------------------
/src/nodes/scenes.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 |
3 | function getSceneInNodes(scene) {
4 | var nodes = [];
5 | RED.nodes.eachNode(n => {
6 | try {
7 | if (n.type === "scene-in" && n.scene === scene) {
8 | nodes.push(RED.nodes.getNode(n.id));
9 | }
10 | } catch (err) {
11 | console.log(err);
12 | }
13 | });
14 |
15 | return nodes;
16 | }
17 |
18 | function sceneIn(config) {
19 | RED.nodes.createNode(this, config);
20 | var node = this;
21 |
22 | this.isActive = function () {
23 | return config.active;
24 | };
25 |
26 | this.trigger = function (msg) {
27 | if (config.active === true) {
28 | msg.scene = config.scene;
29 | node.send(msg);
30 | }
31 | };
32 | }
33 | RED.nodes.registerType("scene-in", sceneIn);
34 |
35 | function sceneSelector(config) {
36 | RED.nodes.createNode(this, config);
37 | var node = this;
38 |
39 | if (config.scenes.length === 0) {
40 | node.status({ fill: "red", text: "no scenes configured" });
41 | return;
42 | }
43 |
44 | var nodeContext = this.context();
45 | if (!nodeContext.get("index")) {
46 | nodeContext.set("index", - 1);
47 | }
48 |
49 | function setState(index) {
50 | var scene = index == -1 ? "---" : config.scenes[index];
51 | node.status({
52 | fill: "yellow",
53 | text: "Idx: " + index + ", Scene: " + scene
54 | });
55 | }
56 |
57 | var index = nodeContext.get("index");
58 | setState(index);
59 |
60 | node.on("input",msg => handleMessage(msg, 0));
61 |
62 | function handleMessage(msg, revCount) {
63 | if(revCount >= config.scenes.length ){
64 | node.error("All configured scenes are inactive");
65 | return;
66 | }
67 | revCount++;
68 |
69 | var command = msg.command !== undefined ? msg.command : msg.payload.command;
70 | var scene = msg.scene;
71 |
72 | if(!scene && msg.payload) {
73 | scene = msg.payload.scene;
74 | }
75 |
76 | if (!command) {
77 | return;
78 | }
79 |
80 | switch (command) {
81 | case "next":
82 | index++;
83 | break;
84 | case "previous":
85 | case "prev":
86 | index--;
87 | break;
88 | case "set":
89 | if (typeof scene === "number" && scene < config.scenes.length && scene >= 0) {
90 | index = scene;
91 | } else if (typeof scene === "string") {
92 | index = config.scenes.indexOf(scene);
93 | if (index < 0) {
94 | node.error("invalid scene");
95 | return;
96 | }
97 | } else {
98 | node.error("invalid scene");
99 | return;
100 | }
101 | break;
102 | default:
103 | node.error("Command not found");
104 | return;
105 | }
106 |
107 | if (config.wrapAround === false) {
108 | index = Math.min(config.scenes.length - 1, index);
109 | index = Math.max(0, index);
110 | } else {
111 | if (index >= config.scenes.length) {
112 | index = 0;
113 | } else if (index < 0) {
114 | index = config.scenes.length - 1;
115 | }
116 | }
117 |
118 | if (config.changedOutputOnly === false || nodeContext.get("index") != index) {
119 | var nodes = getSceneInNodes(config.scenes[index]).filter(n => n.isActive());
120 |
121 | if (nodes.length === 0) {
122 | if (command === "next" || command === "previous") {
123 | handleMessage(msg, revCount);
124 | } else {
125 | node.error(`No active node for scene "${config.scenes[index]}" found.`);
126 | return;
127 | }
128 | }
129 |
130 | msg.command = undefined;
131 | nodes.forEach(n => {
132 | n.trigger(msg);
133 | });
134 |
135 | nodeContext.set("index", index);
136 | setState(index);
137 | }
138 | }
139 | }
140 | RED.nodes.registerType("scene-selector", sceneSelector);
141 | };
--------------------------------------------------------------------------------
/src/nodes/sensors.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable quotes */
2 | module.exports = function (RED) {
3 | const utils = require("../lib/utils.js");
4 | const bavaria = utils.bavaria();
5 |
6 | function contactSensor(config) {
7 | RED.nodes.createNode(this, config);
8 | var bridgeNode = RED.nodes.getNode(config.bridge);
9 | var node = this;
10 |
11 | utils.setConnectionState(bridgeNode, node);
12 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
13 | node.status({ fill: "green", text: "connected" });
14 |
15 | bridgeNode.subscribeDevice(node.id, config.deviceName, function (message) {
16 | if (message.contact) {
17 | node.send({ payload: message });
18 | } else {
19 | node.send([null, { payload: message }]);
20 | }
21 | });
22 | });
23 |
24 | node.on("close", () => {
25 | bavaria.observer.unregister(regId);
26 | });
27 | }
28 | RED.nodes.registerType("contact-sensor", contactSensor);
29 |
30 | function occupancySensor(config) {
31 | RED.nodes.createNode(this, config);
32 | var bridgeNode = RED.nodes.getNode(config.bridge);
33 | var node = this;
34 |
35 | utils.setConnectionState(bridgeNode, node);
36 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
37 | node.status({ fill: "green", text: "connected" });
38 |
39 | bridgeNode.subscribeDevice(node.id, config.deviceName, function (message) {
40 | if (message.occupancy) {
41 | node.send({ payload: message });
42 | } else {
43 | node.send([null, { payload: message }]);
44 | }
45 | });
46 | });
47 |
48 | node.on("close", () => {
49 | bavaria.observer.unregister(regId);
50 | });
51 | }
52 | RED.nodes.registerType("occupancy-sensor", occupancySensor);
53 |
54 | function climateSensor(config) {
55 | RED.nodes.createNode(this, config);
56 | var bridgeNode = RED.nodes.getNode(config.bridge);
57 | var node = this;
58 | utils.setConnectionState(bridgeNode, node);
59 |
60 | function messageReceived(message) {
61 | var outputs = [message];
62 |
63 | var text = "";
64 | if (config.temperature === true) {
65 | text += "T: " + message.temperature + "C° ";
66 | outputs.push({ payload: message.temperature, device_name: config.deviceName });
67 | }
68 |
69 | if (config.pressure === true) {
70 | text += "P: " + message.pressure + "mBar ";
71 | outputs.push({ payload: message.pressure, device_name: config.deviceName });
72 | }
73 |
74 | if (config.humidity === true) {
75 | text += "H: " + message.humidity + "%";
76 | outputs.push({ payload: message.humidity, device_name: config.deviceName });
77 | }
78 |
79 | if (config.co2 === true) {
80 | text += "CO2: " + message.co2 + "ppm";
81 | outputs.push({ payload: message.co2, device_name: config.deviceName });
82 | }
83 |
84 | node.status({ fill: "green", text: text });
85 | if (config.separateOutputs === true) {
86 | node.send(outputs);
87 |
88 | } else {
89 | node.send({ payload: message, device_name: config.deviceName });
90 | }
91 | }
92 |
93 | function subscribe() {
94 | node.status({ fill: "green", text: "connected" });
95 | bridgeNode.subscribeDevice(node.id, config.deviceName, messageReceived);
96 | }
97 |
98 | if (bridgeNode.isConnected() === true) {
99 | subscribe();
100 | }
101 |
102 | const regId = bavaria.observer.register(bridgeNode.id + "_connected", function (message) {
103 | subscribe();
104 | });
105 |
106 | node.on('close', function () {
107 | bridgeNode.unsubscribe(node.id);
108 | bavaria.observer.unregister(regId);
109 | });
110 | }
111 | RED.nodes.registerType("climate-sensor", climateSensor);
112 | };
--------------------------------------------------------------------------------
/src/non-z2m-nodes/devices-shelly-25.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | const utils = require("../lib/utils.js");
3 | const bavaria = utils.bavaria();
4 |
5 | function createShellyConfig(config) {
6 | RED.nodes.createNode(this, config);
7 | this.prefix = config.prefix;
8 | this.name = config.name;
9 |
10 | }
11 |
12 | RED.nodes.registerType("shelly-config", createShellyConfig);
13 |
14 | function createShelly25(config) {
15 | RED.nodes.createNode(this, config);
16 | const node = this;
17 | const broker = RED.nodes.getNode(config.mqtt);
18 | const shelly = RED.nodes.getNode(config.shelly);
19 |
20 | const context = node.context();
21 | const status = context.get("status") || { relay: [{ state: "off", energy: 0, power: 0 }, { state: "off", energy: 0, power: 0 }] };
22 | const channel = parseInt(config.channel);
23 |
24 | let subscribedTopics = [];
25 |
26 | broker.register(node);
27 |
28 | function subscribe(channel) {
29 | subscribeRaw(node.id, `${shelly.prefix}/relay/${channel}/power`, (msg) => { setPower(channel, msg); });
30 | subscribeRaw(node.id, `${shelly.prefix}/relay/${channel}`, (msg) => { setRelay(channel, msg); });
31 | subscribeRaw(node.id, `${shelly.prefix}/relay/${channel}/energy`, (msg) => { setEnergy(channel, msg); });
32 | subscribeRaw(node.id, `${shelly.prefix}/input/${channel}`, (msg) => { inputReceived(msg, channel); });
33 | }
34 |
35 | function subscribeRaw(id, topic, callback)
36 | {
37 | broker.subscribe(topic, 0, (topic, payload, packet) => {
38 | callback(payload.toString("utf8"));
39 | }, id);
40 | subscribedTopics.push(topic);
41 | }
42 |
43 | /**
44 | * Unsubscribe from all topics
45 | */
46 | function unsubscribeAll(){
47 | for(const topic of subscribedTopics){
48 | broker.unsubscribe(topic, node.id, true);
49 | }
50 |
51 | subscribedTopics = [];
52 | }
53 |
54 | if (channel === 2) {
55 | subscribe(0);
56 | subscribe(1);
57 | } else {
58 | subscribe(channel, 0);
59 | }
60 |
61 | function inputReceived(msg, channel) {
62 | msg = {
63 | payload: msg
64 | };
65 | if (config.customPayload === true) {
66 | const data = config["payloadInput" + channel];
67 | const type = config["typeInput" + channel];
68 | msg.payload = utils.ui.input.getPayload(data, type);
69 | }
70 |
71 | node.send(utils.outputs.preapreOutputFor(1, msg));
72 | }
73 |
74 | function setRelay(index, value) {
75 | status.relay[index].state = value;
76 | setStatus(status, index);
77 | }
78 |
79 | function setEnergy(index, value) {
80 | status.relay[index].energy = value;
81 | setStatus(status, index);
82 | }
83 |
84 | function setPower(index, value) {
85 | status.relay[index].power = value;
86 | setStatus(status, index);
87 | }
88 |
89 | function setStatus(status, index) {
90 | context.set("status", status);
91 | publishState(index);
92 | }
93 |
94 | function publishState(index) {
95 | if (channel === 2) {
96 | node.send({ payload: status.relay });
97 | } else {
98 | node.send({ payload: status.relay[index] });
99 | }
100 | }
101 |
102 | node.on("input", function (msg) {
103 | function getOutputPayload(msg, channel, state) {
104 | return utils.payloads.devices.addDevice(msg, {
105 | topic: `${shelly.prefix}/relay/${channel}/command`,
106 | state: utils.payloads.convertToOnOff(state),
107 | target: "mqtt",
108 | payloadGenerator: preparePayload
109 | });
110 | }
111 |
112 | function getNewState(channel) {
113 | if (config.state === "toggle") {
114 | return status.relay[channel].state == "on" ? "off" : "on";
115 | }
116 |
117 | return config.state;
118 | }
119 |
120 | if (channel === 2) {
121 | msg = getOutputPayload(msg, 0, getNewState(0));
122 | msg = getOutputPayload(msg, 1, getNewState(1));
123 | } else {
124 | msg = getOutputPayload(msg, channel, getNewState(channel));
125 | }
126 |
127 | node.send(utils.outputs.preapreOutputFor(this.wires.length - 1, msg));
128 | });
129 |
130 | function preparePayload(data) {
131 | if (data.state === undefined) {
132 | data.state = "off";
133 | }
134 |
135 | return data.state.toLowerCase();
136 | }
137 |
138 | node.on("close", function () {
139 | unsubscribeAll();
140 | broker.deregister(node, ()=>{});
141 | });
142 | }
143 |
144 | RED.nodes.registerType("shelly-25", createShelly25);
145 | };
--------------------------------------------------------------------------------
/src/ota.html:
--------------------------------------------------------------------------------
1 |
2 |
76 |
77 |
113 |
114 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import { MqttClient } from "mqtt";
2 | import { Node, NodeCredentials, NodeDef, NodeMessage } from "node-red";
3 | import { Z2mDeviceEntry } from "./device-types"
4 |
5 | export interface DeviceConfigOptions {
6 | name: string,
7 | bridge: string
8 | // bridge: { value: "", type: "zigbee2mqtt-bridge-config" },
9 | deviceName: string
10 | brightnessSupport: boolean
11 | temperatureSupport: boolean
12 | colorSupport: boolean
13 | genericMqttDevice: boolean
14 | statusTopic: string
15 | commandTopic: string
16 | refreshTopic: string
17 | }
18 |
19 | export interface DeviceConfigNodeDef extends NodeDef, DeviceConfigOptions { }
20 |
21 | export interface DeviceConfigNode extends Node {
22 | bridge: string
23 | deviceName: string
24 | brightnessSupport: boolean
25 | temperatureSupport: boolean
26 | colorSupport: boolean
27 | genericMqttDevice: boolean
28 | statusTopic: string
29 | commandTopic: string
30 | refreshTopic: string
31 | }
32 |
33 | export interface BridgeConfigOptions {
34 |
35 | }
36 |
37 | export interface BridgeConfigCredentials {
38 | username: string;
39 | password: string;
40 | }
41 |
42 | export const BridgeConfigCredentials: NodeCredentials = {
43 | username: { type: "text" },
44 | password: { type: "password" },
45 | };
46 |
47 | export interface BridgeConfigOptions {
48 | name: string
49 | mqtt: string
50 | broker: string
51 | baseTopic: string // default "zigbee2mqtt"
52 | enabledLogging: boolean
53 | allowDeviceStatusRefresh: boolean
54 | }
55 |
56 | export interface BridgeConfigNode extends Node {
57 | isConnected: MqttConfigNode["isConnected"]
58 | isReconnecting: MqttConfigNode["isReconnecting"]
59 | baseTopic: string
60 | publish: MqttConfigNode["publish"]
61 | knownDevices: Array
62 | getDeviceList: (callback: () => void) => Array
63 | subscribeDevice: MqttConfigNode["subscribeDevice"]
64 | publishDevice: (device: string, msg: string | any) => void
65 | subscribe: MqttConfigNode["subscribe"]
66 | unsubscribe: MqttConfigNode["unsubscribe"]
67 | setDeviceState: (device: string | undefined, payload: string) => void
68 | refreshDevice: (deviceName: string, force:boolean) => void
69 | registerOtaNode: (nodeId: string, otaStatusCallback: OtaStatusCallback, deviceStatusCallback: DeviceStatusCallback) => void
70 | }
71 |
72 | export interface BridgeConfigNodeDef extends NodeDef, BridgeConfigOptions { }
73 |
74 | export type OtaStatusCallback = (msg: any) => void
75 | export type DeviceStatusCallback = (deviceName: string, msg: any) => void
76 |
77 | export interface MqttConfigCredentials {
78 | username: string;
79 | password: string;
80 | }
81 |
82 | export const MqttConfigCredentials: NodeCredentials = {
83 | username: { type: "text" },
84 | password: { type: "password" },
85 | };
86 |
87 | // TODO: Switch to core-mqtt from node-red
88 | export type MqttConfigOptions = {
89 | name: string
90 | protocol?: string // default: mqtt
91 | broker: string // default: localhost
92 | requireLogin: boolean
93 | }
94 |
95 | export interface NodeMqttBroker extends Node {
96 | register: (mqttNode: Node) => void;
97 | deregister: (mqttNode: Node, done: () => void) => void;
98 | subscribe: (topic: string, qos: number, callback: NodeMqttBrokerMessageCallback, ref : any) => void;
99 | publish: (topic: NodeMessage, done?: () => void) => void;
100 | connected: boolean
101 | }
102 |
103 | export type NodeMqttMessage = NodeMessage & {
104 | topic: string,
105 | retain: boolean
106 | qos:number
107 | }
108 |
109 | export type NodeMqttBrokerMessageCallback = (topic: string, payload: any, packet: any) => void;
110 |
111 | export interface MqttConfigNode extends Node {
112 | broker: string
113 | requireLogin: boolean
114 | isConnected: () => MqttClient['connected']
115 | isReconnecting: () => MqttClient['reconnecting']
116 | publish: (topic: string, message: string | Buffer) => void // MqttClient['publish'] (return type void instant of client)
117 | mqttClient: MqttClient
118 | subscribeDevice: (nodeId: string, topic: string, callback: MqttConfigCallback) => void
119 | subscribe: (nodeId: string, topic: string, callback: MqttConfigCallback, jsonPayload?: boolean) => void
120 | unsubscribe: (nodeId: string) => void
121 | }
122 |
123 | export interface MqttConfigNodeDef extends NodeDef, MqttConfigOptions { }
124 |
125 | export type MqttConfigCallback = (message: any, topic: string) => void
126 |
127 | export type MqttConfigSubsType = {
128 | nodeId: string,
129 | topic: string,
130 | callback: MqttConfigCallback
131 | isDevice: boolean
132 | }
133 |
134 | export type Z2mDeviceContextObsolete = {
135 | ieeeAddr: string,
136 | friendly_name: string,
137 | model: string,
138 | vendor: string
139 | type: string
140 | }
141 |
142 | export type Z2mDevice = {
143 | type: string,
144 | ieee_address: string,
145 | friendly_name: string,
146 | definition: Z2mDeviceDefinition,
147 | }
148 |
149 | export type Z2mDeviceDefinition = {
150 | model: string,
151 | vendor: string
152 | }
--------------------------------------------------------------------------------
/src/zigbee2mqtt-config.html:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
46 |
47 |
59 |
60 |
61 |
107 |
108 |
--------------------------------------------------------------------------------
/test-integration/docker/.gitignore:
--------------------------------------------------------------------------------
1 | mosquitto.log
--------------------------------------------------------------------------------
/test-integration/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.9"
2 |
3 | services:
4 | mosquitto:
5 | image: eclipse-mosquitto:latest
6 | volumes:
7 | - ./mosquitto:/mosquitto
8 | - ./mosquitto/data:/mosquitto/data
9 | - ./mosquitto/log:/mosquitto/log
10 | ports:
11 | - "1883:1883"
12 | restart: unless-stopped
--------------------------------------------------------------------------------
/test-integration/docker/mosquitto/config/mosquitto.conf:
--------------------------------------------------------------------------------
1 | persistence false
2 |
3 | log_dest file /mosquitto/log/mosquitto.log
4 |
5 | connection_messages true
6 | allow_anonymous true
7 |
8 | listener 1883
--------------------------------------------------------------------------------
/test/scenes_scene-selector_spec.js:
--------------------------------------------------------------------------------
1 | var should = require("should");
2 | var helper = require("node-red-node-test-helper");
3 | var sceneSelectorNode = require("../src/nodes/scenes.js");
4 |
5 | helper.init(require.resolve("node-red"));
6 |
7 | describe("scene-selector Node", function () {
8 |
9 | beforeEach(function (done) {
10 | helper.startServer(done);
11 | });
12 |
13 | afterEach(function (done) {
14 | helper.unload();
15 | helper.stopServer(done);
16 | });
17 |
18 | it("should be loaded", function (done) {
19 | var flow = [{ id: "n1", type: "scene-in", name: "scene-selector" }];
20 | helper.load(sceneSelectorNode, flow, function () {
21 | var n1 = helper.getNode("n1");
22 | try {
23 | n1.should.have.property("name", "scene-selector");
24 | done();
25 | } catch(err) {
26 | done(err);
27 | }
28 | });
29 | });
30 |
31 | });
--------------------------------------------------------------------------------
/test/zigbee2mqtt_generic-lamp_spec.js:
--------------------------------------------------------------------------------
1 | const should = require("should");
2 | const helper = require("node-red-node-test-helper");
3 | const requiredNodes = [
4 | require("../dist/zigbee2mqtt.js"),
5 | require("../dist/zigbee2mqtt-config.js"),
6 | require("@node-red/nodes/core/common/20-inject.js"),
7 | require("@node-red/nodes/core/common/21-debug.js"),
8 | require("@node-red/nodes/core/network/10-mqtt.js"),
9 | ];
10 |
11 | helper.init(require.resolve("node-red"));
12 |
13 | describe("generic-lamp Node", function () {
14 |
15 | beforeEach(function (done) {
16 | helper.startServer(done);
17 | });
18 |
19 | afterEach(function (done) {
20 | helper.unload();
21 | helper.stopServer(done);
22 | });
23 |
24 | it("should be loaded", function (done) {
25 | var flow = [
26 | {
27 | "id": "caa96857ce2cb799",
28 | "type": "generic-lamp",
29 | "z": "b46cd66b87ed23ae",
30 | "device": "7716bf2cc109979b",
31 | "name": "Ikea test lamp",
32 | "state": "ON",
33 | "brightness": 0,
34 | "temperature": 50,
35 | "red": 0,
36 | "green": 0,
37 | "blue": 0,
38 | "transition": 2,
39 | "delay": 0,
40 | "x": 480,
41 | "y": 160,
42 | "wires": [
43 | [
44 | "fd3fb7b09d9d0524"
45 | ]
46 | ]
47 | },
48 | {
49 | "id": "fd3fb7b09d9d0524",
50 | "type": "debug",
51 | "z": "b46cd66b87ed23ae",
52 | "name": "",
53 | "active": true,
54 | "tosidebar": true,
55 | "console": false,
56 | "tostatus": false,
57 | "complete": "false",
58 | "statusVal": "",
59 | "statusType": "auto",
60 | "x": 690,
61 | "y": 160,
62 | "wires": []
63 | },
64 | {
65 | "id": "ec3f8c76406956ef",
66 | "type": "inject",
67 | "z": "b46cd66b87ed23ae",
68 | "name": "",
69 | "props": [
70 | {
71 | "p": "payload"
72 | },
73 | {
74 | "p": "topic",
75 | "vt": "str"
76 | }
77 | ],
78 | "repeat": "",
79 | "crontab": "",
80 | "once": false,
81 | "onceDelay": 0.1,
82 | "topic": "",
83 | "payload": "",
84 | "payloadType": "date",
85 | "x": 280,
86 | "y": 160,
87 | "wires": [
88 | [
89 | "caa96857ce2cb799"
90 | ]
91 | ]
92 | },
93 | {
94 | "id": "7716bf2cc109979b",
95 | "type": "zigbee2mqtt-device-config",
96 | "name": "Ikea test lamp",
97 | "bridge": "f8ba4931243895a8",
98 | "deviceName": "Ikea Test Lamp",
99 | "brightnessSupport": false,
100 | "temperatureSupport": false,
101 | "colorSupport": false,
102 | "genericMqttDevice": false,
103 | "statusTopic": "",
104 | "commandTopic": "",
105 | "refreshTopic": ""
106 | },
107 | {
108 | "id": "f8ba4931243895a8",
109 | "type": "zigbee2mqtt-bridge-config",
110 | "name": "z2m2",
111 | "broker": "eb1b1a7d89c241b8",
112 | "baseTopic": "zigbee2mqtt",
113 | "enabledLogging": false,
114 | "allowDeviceStatusRefresh": true
115 | },
116 | {
117 | "id": "eb1b1a7d89c241b8",
118 | "type": "mqtt-broker",
119 | "name": "mosquitto",
120 | "broker": "192.168.178.49",
121 | "port": "1883",
122 | "clientid": "",
123 | "autoConnect": true,
124 | "usetls": false,
125 | "protocolVersion": "4",
126 | "keepalive": "60",
127 | "cleansession": true,
128 | "birthTopic": "",
129 | "birthQos": "0",
130 | "birthPayload": "",
131 | "birthMsg": {},
132 | "closeTopic": "",
133 | "closeQos": "0",
134 | "closePayload": "",
135 | "closeMsg": {},
136 | "willTopic": "",
137 | "willQos": "0",
138 | "willPayload": "",
139 | "willMsg": {},
140 | "sessionExpiry": ""
141 | }
142 | ];
143 | helper.load(requiredNodes, flow, function () {
144 | var n1 = helper.getNode("caa96857ce2cb799");
145 | try {
146 | n1.should.have.property("name", "Ikea test lamp");
147 | done();
148 | } catch (err) {
149 | done(err);
150 | }
151 | });
152 | });
153 |
154 |
155 | });
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // until we are done, allow plain JS
11 | "allowJs": true, /* Allow javascript files to be compiled. */
12 | // "checkJs": true, /* Report errors in .js files. */
13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
14 | "declaration": false, /* Generates corresponding '.d.ts' file. */
15 | "declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */
16 | // "sourceMap": true, /* Generates corresponding '.map' file. */
17 | // "outFile": "./", /* Concatenate and emit output to single file. */
18 | "outDir": "./dist", /* Redirect output structure to the directory. */
19 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
20 | // "composite": true, /* Enable project compilation */
21 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
22 | // "removeComments": true, /* Do not emit comments to output. */
23 | // "noEmit": true, /* Do not emit outputs. */
24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
27 |
28 | /* Strict Type-Checking Options */
29 | "strict": true, /* Enable all strict type-checking options. */
30 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
31 | // "strictNullChecks": true, /* Enable strict null checks. */
32 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
33 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
34 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
35 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
36 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
37 |
38 | /* Additional Checks */
39 | // "noUnusedLocals": true, /* Report errors on unused locals. */
40 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
41 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
42 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
43 |
44 | /* Module Resolution Options */
45 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
49 | // "typeRoots": [], /* List of folders to include type definitions from. */
50 | // "types": [], /* Type declaration files to be included in compilation. */
51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
55 |
56 | /* Source Map Options */
57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
61 |
62 | /* Experimental Options */
63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
65 |
66 | /* Advanced Options */
67 | "skipLibCheck": true, /* Skip type checking of declaration files. */
68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
69 | },
70 | "include": [
71 | "src/**/*"
72 | ],
73 | "exclude": [
74 | "node_modules"
75 | ]
76 | }
77 |
--------------------------------------------------------------------------------
/upcoming-changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog for the upcoming version
2 | > This document is an internal note for the changelog of the upcoming release.
3 |
4 | > If new features are added, increase the minor version || if bug fixes are added, increase the patch version.
5 |
6 | ### Release: `0.19.7`
7 |
8 | #### Features:
9 |
10 | #### Bug fixes:
11 |
12 |
13 | #### Behind the scenes
14 |
--------------------------------------------------------------------------------