node-red-contrib-esphome 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +41 -0
- package/.github/dependabot.yml +7 -0
- package/.gitignore +10 -0
- package/.prettierrc.json +10 -0
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/build/lib/utils.d.ts +4 -0
- package/build/lib/utils.js +80 -0
- package/build/lib/utils.js.map +1 -0
- package/build/nodes/api.d.ts +1 -0
- package/build/nodes/api.html +0 -0
- package/build/nodes/api.js +34 -0
- package/build/nodes/api.js.map +1 -0
- package/build/nodes/device.d.ts +1 -0
- package/build/nodes/device.html +147 -0
- package/build/nodes/device.js +104 -0
- package/build/nodes/device.js.map +1 -0
- package/build/nodes/icons/esphome.svg +1 -0
- package/build/nodes/in.d.ts +1 -0
- package/build/nodes/in.html +125 -0
- package/build/nodes/in.js +70 -0
- package/build/nodes/in.js.map +1 -0
- package/build/nodes/out.d.ts +1 -0
- package/build/nodes/out.html +119 -0
- package/build/nodes/out.js +81 -0
- package/build/nodes/out.js.map +1 -0
- package/examples/SwitchOFForON.json +103 -0
- package/package.json +56 -0
- package/readme/device.png +0 -0
- package/readme/flow.png +0 -0
- package/src/lib/utils.ts +69 -0
- package/src/nodes/api.ts +27 -0
- package/src/nodes/device.ts +118 -0
- package/src/nodes/in.ts +83 -0
- package/src/nodes/out.ts +83 -0
- package/tsconfig.json +20 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"node": true,
|
|
4
|
+
"es6": true
|
|
5
|
+
},
|
|
6
|
+
"extends": [
|
|
7
|
+
"eslint:recommended",
|
|
8
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
9
|
+
"plugin:@typescript-eslint/recommended",
|
|
10
|
+
"plugin:prettier/recommended",
|
|
11
|
+
"prettier"
|
|
12
|
+
],
|
|
13
|
+
"plugins": [
|
|
14
|
+
"@typescript-eslint",
|
|
15
|
+
"prettier"
|
|
16
|
+
],
|
|
17
|
+
"globals": {
|
|
18
|
+
"Atomics": "readonly",
|
|
19
|
+
"SharedArrayBuffer": "readonly"
|
|
20
|
+
},
|
|
21
|
+
"parser": "@typescript-eslint/parser",
|
|
22
|
+
"parserOptions": {
|
|
23
|
+
"ecmaVersion": 2020
|
|
24
|
+
},
|
|
25
|
+
"rules": {
|
|
26
|
+
"prettier/prettier": "error",
|
|
27
|
+
"semi": "error",
|
|
28
|
+
"linebreak-style": ["error", "unix"],
|
|
29
|
+
"quotes": [
|
|
30
|
+
"error",
|
|
31
|
+
"single",
|
|
32
|
+
{ "avoidEscape": true }
|
|
33
|
+
],
|
|
34
|
+
"indent": [
|
|
35
|
+
"error",
|
|
36
|
+
2
|
|
37
|
+
],
|
|
38
|
+
"@typescript-eslint/no-explicit-any": "off"
|
|
39
|
+
},
|
|
40
|
+
"ignorePatterns":["build"]
|
|
41
|
+
}
|
package/.gitignore
ADDED
package/.prettierrc.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 D
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# node-red-contrib-esphome
|
|
2
|
+
|
|
3
|
+
[](https://nodered.org)
|
|
4
|
+
[](https://nodejs.org/en/)
|
|
5
|
+
[](https://www.npmjs.com/package/node-red-contrib-esphome)
|
|
6
|
+
[](https://github.com/twocolors/node-red-contrib-esphome/stargazers)
|
|
7
|
+
[](https://packagequality.com/#?package=node-red-contrib-esphome)
|
|
8
|
+
|
|
9
|
+
[](https://github.com/twocolors/node-red-contrib-esphome/issues)
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## About
|
|
16
|
+
|
|
17
|
+
### !!! Alpha, Alpha, Alpha release
|
|
18
|
+
### !!! Need help writing documentation
|
|
19
|
+
|
|
20
|
+
Node-RED nodes to ESPhome devices
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
$ npm i node-red-contrib-esphome
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Inputs
|
|
29
|
+
|
|
30
|
+
#### Button
|
|
31
|
+
#### Climate
|
|
32
|
+
- `mode` - optional. 0 - OFF, 1 - AUTO, 2 - COOL, 3 - HEAT, 4 - FAN_ONLY, 5 - DRY. See `supportedModesList` attr in config
|
|
33
|
+
- `targetTemperature`- optional. float
|
|
34
|
+
- `targetTemperatureLow`- optional. float
|
|
35
|
+
- `targetTemperatureHigh`- optional. float
|
|
36
|
+
- `legacyAway` - optional. Boolean. Deprecated: use `preset` with AWAY
|
|
37
|
+
- `fanMode` - optional. 0 - ON, 1 - OFF, 2 - AUTO, 3 - LOW, 4 - MEDIUM, 5 - HIGH, 6 - MIDDLE, 7 - FOCUS, 8 - DIFFUSE. See `supportedFanModesList` attr in config
|
|
38
|
+
- `swingMode` - optional. 0 - OFF, 1 - BOTH, 2 - VERTICAL, 3 - HORIZONTAL. See `supportedSwingModesList` attr in config
|
|
39
|
+
- `customFanMode` - optional. string. See `supportedCustomFanModesList` attr in config
|
|
40
|
+
- `preset` - optional. 0 - NONE, 1 - HOME, 2 - AWAY, 3 - BOOST, 4 - COMFORT, 5 - ECO, 6 - SLEEP, 7 - ACTIVITY. See `supportedPresetsList` attr in config
|
|
41
|
+
- `customPreset` - optional. string. See `supportedCustomPresetsList` attr in config
|
|
42
|
+
#### Cover
|
|
43
|
+
- `legacyCommand` - optional. 0 - OPEN, 1 - CLOSE, 2 - STOP. Deprecated: use `position`
|
|
44
|
+
- `position` - optional. float. 0.0 - CLOSED, 1.0 - OPEN. See `supportsPosition` attr in config
|
|
45
|
+
- `tilt` - optional. float. 0.0 - CLOSED, 1.0 - OPEN. See `supportsTilt` attr in config
|
|
46
|
+
- `stop` - optional. boolean
|
|
47
|
+
#### Fan
|
|
48
|
+
- `state` - optional. boolean
|
|
49
|
+
- `speed` - optional. 0 - LOW, 1 - MEDIUM, 2 - HIGH
|
|
50
|
+
- `oscillating` - optional. boolean
|
|
51
|
+
- `direction` - optional. 0 - FORWARD, 1 - REVERSE
|
|
52
|
+
- `speedLevel` - optional. integer. See `supportedSpeedLevels` attr in config
|
|
53
|
+
#### Light
|
|
54
|
+
- `state` - optional. boolean
|
|
55
|
+
- `brightness` - optional. float
|
|
56
|
+
- `red` - optional. integer 0-255
|
|
57
|
+
- `green` - optional. integer 0-255
|
|
58
|
+
- `blue` - optional. integer 0-255
|
|
59
|
+
- `colorMode` - optional. integer. See `supportedColorModesList` attr in config
|
|
60
|
+
- `colorBrightness` - optional. float
|
|
61
|
+
- `white` - optional. integer 0-255
|
|
62
|
+
- `colorTemperature` - optional. integer
|
|
63
|
+
- `coldWhite` - optional. float
|
|
64
|
+
- `warmWhite` - optional. float
|
|
65
|
+
- `flashLength` - optional. integer
|
|
66
|
+
- `effect` - optional. string. effect from effects array in config list
|
|
67
|
+
#### Lock
|
|
68
|
+
- `command` - REQUIRED. 0 - UNLOCK, 1 - LOCK, 2 - OPEN
|
|
69
|
+
- `code` - optional. string. See `requiresCode` attr in config
|
|
70
|
+
#### Number
|
|
71
|
+
- `state` - REQUIRED. float. See `minValue`, `maxValue`, and `step` attrs in config
|
|
72
|
+
#### Select
|
|
73
|
+
- `state` - REQUIRED. string. See `optionsList` attr in config
|
|
74
|
+
#### Siren
|
|
75
|
+
- `state` - REQUIRED. boolean
|
|
76
|
+
- `tone` - optional. string. See `tonesList` attr in config
|
|
77
|
+
- `duration` - optional. integer. See `supportsDuration` attr in config
|
|
78
|
+
- `volume` - optional. integer. See `supportsVolume` attr in config
|
|
79
|
+
#### Switch
|
|
80
|
+
- `state` - REQUIRED. boolean
|
|
81
|
+
|
|
82
|
+
## Pictures
|
|
83
|
+
|
|
84
|
+
<img src="https://github.com/twocolors/node-red-contrib-esphome/raw/main/readme/device.png">
|
|
85
|
+
<img src="https://github.com/twocolors/node-red-contrib-esphome/raw/main/readme/flow.png">
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Status = exports.discovery = void 0;
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
14
|
+
const mdns = require('node-dns-sd');
|
|
15
|
+
// {
|
|
16
|
+
// address: '192.168.0.114',
|
|
17
|
+
// fqdn: 'corridor-waylight._esphomelib._tcp.local',
|
|
18
|
+
// modelName: null,
|
|
19
|
+
// familyName: null,
|
|
20
|
+
// service: { port: 6053, protocol: 'tcp', type: 'esphomelib' },
|
|
21
|
+
// packet: {
|
|
22
|
+
// header: [Object],
|
|
23
|
+
// questions: [],
|
|
24
|
+
// answers: [Array],
|
|
25
|
+
// authorities: [],
|
|
26
|
+
// additionals: [Array],
|
|
27
|
+
// address: '192.168.0.114'
|
|
28
|
+
// }
|
|
29
|
+
// }
|
|
30
|
+
function discovery(name = '_esphomelib._tcp.local', wait = 3) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
return yield new Promise((resolve, reject) => {
|
|
33
|
+
mdns
|
|
34
|
+
.discover({
|
|
35
|
+
name: name,
|
|
36
|
+
wait: wait
|
|
37
|
+
})
|
|
38
|
+
.then((items) => {
|
|
39
|
+
const devices = [];
|
|
40
|
+
items
|
|
41
|
+
.sort((a, b) => (a.fqdn > b.fqdn ? 1 : -1))
|
|
42
|
+
.map((device) => {
|
|
43
|
+
devices.push({
|
|
44
|
+
host: device.address,
|
|
45
|
+
port: device.service.port,
|
|
46
|
+
fqdn: device.fqdn.replace(/_esphomelib\._tcp./gi, '')
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
resolve(devices);
|
|
50
|
+
})
|
|
51
|
+
.catch((e) => {
|
|
52
|
+
reject(e);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
exports.discovery = discovery;
|
|
58
|
+
exports.Status = {
|
|
59
|
+
error: {
|
|
60
|
+
fill: 'red',
|
|
61
|
+
shape: 'dot',
|
|
62
|
+
text: 'error'
|
|
63
|
+
},
|
|
64
|
+
disconnected: {
|
|
65
|
+
fill: 'red',
|
|
66
|
+
shape: 'ring',
|
|
67
|
+
text: 'disconnected'
|
|
68
|
+
},
|
|
69
|
+
connecting: {
|
|
70
|
+
fill: 'yellow',
|
|
71
|
+
shape: 'ring',
|
|
72
|
+
text: 'connecting'
|
|
73
|
+
},
|
|
74
|
+
connected: {
|
|
75
|
+
fill: 'green',
|
|
76
|
+
shape: 'dot',
|
|
77
|
+
text: 'connected'
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,8DAA8D;AAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAEpC,IAAI;AACJ,8BAA8B;AAC9B,sDAAsD;AACtD,qBAAqB;AACrB,sBAAsB;AACtB,kEAAkE;AAClE,cAAc;AACd,wBAAwB;AACxB,qBAAqB;AACrB,wBAAwB;AACxB,uBAAuB;AACvB,4BAA4B;AAC5B,+BAA+B;AAC/B,MAAM;AACN,IAAI;AAEJ,SAAsB,SAAS,CAAC,IAAI,GAAG,wBAAwB,EAAE,IAAI,GAAG,CAAC;;QACvE,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI;iBACD,QAAQ,CAAC;gBACR,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,IAAI;aACX,CAAC;iBACD,IAAI,CAAC,CAAC,KAAoB,EAAE,EAAE;gBAC7B,MAAM,OAAO,GAAkB,EAAE,CAAC;gBAElC,KAAK;qBACF,IAAI,CAAC,CAAC,CAAM,EAAE,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;qBACpD,GAAG,CAAC,CAAC,MAAW,EAAE,EAAE;oBACnB,OAAO,CAAC,IAAI,CAAC;wBACX,IAAI,EAAE,MAAM,CAAC,OAAO;wBACpB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;wBACzB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC;qBACtD,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;gBAEL,OAAO,CAAC,OAAO,CAAC,CAAC;YACnB,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE;gBAClB,MAAM,CAAC,CAAC,CAAC,CAAC;YACZ,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACL,CAAC;CAAA;AA1BD,8BA0BC;AAEY,QAAA,MAAM,GAAyB;IAC1C,KAAK,EAAE;QACL,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,OAAO;KACd;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,cAAc;KACrB;IACD,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,YAAY;KACnB;IACD,SAAS,EAAE;QACT,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,WAAW;KAClB;CACO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const utils_1 = require("../lib/utils");
|
|
13
|
+
module.exports = (RED) => {
|
|
14
|
+
const NODE_PATH = '/esphome/';
|
|
15
|
+
RED.httpAdmin.post(NODE_PATH + 'discovery', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
(0, utils_1.discovery)()
|
|
17
|
+
.then((devices) => {
|
|
18
|
+
res.json(devices);
|
|
19
|
+
})
|
|
20
|
+
.catch((e) => {
|
|
21
|
+
res.json(e);
|
|
22
|
+
});
|
|
23
|
+
}));
|
|
24
|
+
RED.httpAdmin.post(NODE_PATH + 'entities', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const deviceNode = RED.nodes.getNode(req.body.deviceNode);
|
|
26
|
+
if (deviceNode) {
|
|
27
|
+
res.json(deviceNode.entities);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
res.json([]);
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/nodes/api.ts"],"names":[],"mappings":";;;;;;;;;;;AAEA,wCAAuC;AAEvC,MAAM,CAAC,OAAO,GAAG,CAAC,GAAY,EAAE,EAAE;IAChC,MAAM,SAAS,GAAG,WAAW,CAAC;IAE9B,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,GAAG,WAAW,EAAE,CAAO,GAAoB,EAAE,GAAqB,EAAE,EAAE;QAChG,IAAA,iBAAS,GAAE;aACR,IAAI,CAAC,CAAC,OAAsB,EAAE,EAAE;YAC/B,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE;YAClB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACP,CAAC,CAAA,CAAC,CAAC;IAEH,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,GAAG,UAAU,EAAE,CAAO,GAAoB,EAAE,GAAqB,EAAE,EAAE;QAC/F,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAQ,CAAC;QAEjE,IAAI,UAAU,EAAE;YACd,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;SAC/B;aAAM;YACL,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SACd;IACH,CAAC,CAAA,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('esphome-device', {
|
|
3
|
+
category: 'config',
|
|
4
|
+
defaults: {
|
|
5
|
+
name: {},
|
|
6
|
+
host: {value: ''},
|
|
7
|
+
port: {value: 6053, validate: RED.validators.number()}
|
|
8
|
+
},
|
|
9
|
+
paletteLabel: 'device',
|
|
10
|
+
credentials: {
|
|
11
|
+
password: {type: 'password'}
|
|
12
|
+
},
|
|
13
|
+
label: function () {
|
|
14
|
+
return this.name || 'ESPhome';
|
|
15
|
+
},
|
|
16
|
+
oneditprepare: function () {
|
|
17
|
+
let node = this;
|
|
18
|
+
|
|
19
|
+
function toggleSelect() {
|
|
20
|
+
let $val = $('#node-config-input-host').val();
|
|
21
|
+
$('#node-config-input-discover').html(`<i class="fa fa-search"></i>`);
|
|
22
|
+
$('#node-config-input-host').off('change');
|
|
23
|
+
$('#node-config-input-host').replaceWith(
|
|
24
|
+
`<input type="text" id="node-config-input-host" style="width: 100%;">`
|
|
25
|
+
);
|
|
26
|
+
$('#node-config-input-host').val($val);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function toggleInput() {
|
|
30
|
+
let $val = $('#node-config-input-host').val();
|
|
31
|
+
let $password = $('#node-config-input-password').val();
|
|
32
|
+
|
|
33
|
+
RED.notify(`Discovery ESPhome devices ... (~5 sec)`);
|
|
34
|
+
|
|
35
|
+
$('#node-config-input-host').prop('disabled', true);
|
|
36
|
+
$.post('esphome/discovery')
|
|
37
|
+
.done(function (data) {
|
|
38
|
+
if (typeof data.message !== 'undefined') {
|
|
39
|
+
RED.notify(data.message, 'error');
|
|
40
|
+
$('#node-config-input-host').prop('disabled', false);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (data.length === 0) {
|
|
45
|
+
RED.notify(`No ESPhome devices found`, 'error');
|
|
46
|
+
$('#node-config-input-host').prop('disabled', false);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$('#node-config-input-discover').html(`<i class="fa fa-i-cursor"></i>`);
|
|
51
|
+
$('#node-config-input-host').replaceWith(
|
|
52
|
+
`<select id="node-config-input-host" style="width: 100%;"></select>`
|
|
53
|
+
);
|
|
54
|
+
data.map(function (d) {
|
|
55
|
+
$('#node-config-input-host').append(
|
|
56
|
+
`<option value="${d.host}" data-host="${d.host}" data-port="${d.port}" data-fqdn="${d.fqdn}">${d.fqdn}</option>`
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
$('#node-config-input-host').on('change', changeHost);
|
|
61
|
+
if (!$val || data.length === 1) {
|
|
62
|
+
$val = data[0].host;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
$('#node-config-input-host').val($val);
|
|
66
|
+
$('#node-config-input-host').prop('disabled', false);
|
|
67
|
+
|
|
68
|
+
if ($val) {
|
|
69
|
+
changeHost();
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
.fail(function (jqxhr, textStatus, error) {
|
|
73
|
+
RED.notify(`Request: ${textStatus}, ${error}`, 'error');
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function changeHost() {
|
|
78
|
+
let $elm = $('#node-config-input-host option:selected');
|
|
79
|
+
|
|
80
|
+
$('#node-config-input-port').val($elm.data('port'));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
$('#node-config-input-discover').click(function () {
|
|
84
|
+
if ($('#node-config-input-host').prop('tagName') === 'INPUT') {
|
|
85
|
+
toggleInput();
|
|
86
|
+
} else {
|
|
87
|
+
toggleSelect();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
oneditsave: function () {
|
|
92
|
+
let $elm = $('#node-config-input-host option:selected');
|
|
93
|
+
if (!$('#node-config-input-name').val()) {
|
|
94
|
+
$('#node-config-input-name').val($elm.data('fqdn'));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<script type="text/html" data-template-name="esphome-device">
|
|
101
|
+
<div class="form-row">
|
|
102
|
+
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
103
|
+
<input type="text" id="node-config-input-name" placeholder="Name" />
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="form-row">
|
|
107
|
+
<label for="node-config-input-host"><i class="fa fa-globe"></i> Host</label>
|
|
108
|
+
<div style="display: inline-block; position: relative; width: 70%; height: 24px;">
|
|
109
|
+
<div style="position: absolute; left: 0; right: 42px;">
|
|
110
|
+
<input type="text" id="node-config-input-host" style="width: 100%;" placeholder="Host" />
|
|
111
|
+
</div>
|
|
112
|
+
<a
|
|
113
|
+
id="node-config-input-discover"
|
|
114
|
+
class="editor-button"
|
|
115
|
+
style="position: absolute; right: 0; top: 0; width: 33px;"
|
|
116
|
+
><i class="fa fa-search"></i
|
|
117
|
+
></a>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="form-row">
|
|
122
|
+
<label for="node-config-input-port"><i class="fa fa-sign-in"></i> Port</label>
|
|
123
|
+
<input type="text" id="node-config-input-port" placeholder="Port" />
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div class="form-row">
|
|
127
|
+
<label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
|
|
128
|
+
<input type="password" id="node-config-input-password" placeholder="Password" />
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div class="form-row">
|
|
132
|
+
<div class="form-tips"><b>Important:</b> Deploy device node to get entities list</div>
|
|
133
|
+
</div>
|
|
134
|
+
</script>
|
|
135
|
+
|
|
136
|
+
<script type="text/html" data-help-name="esphome-device">
|
|
137
|
+
<p>Connection to ESPhome device</p>
|
|
138
|
+
<h3>Configuration</h3>
|
|
139
|
+
<dl class="message-properties">
|
|
140
|
+
<dt class="required">Host<span class="property-type">string</span></dt>
|
|
141
|
+
<dd>host or ip address</dd>
|
|
142
|
+
<dt class="required">Port<span class="property-type">number</span></dt>
|
|
143
|
+
<dd>port</dd>
|
|
144
|
+
<dt class="required">Password<span class="property-type">password</span></dt>
|
|
145
|
+
<dd>password for API</dd>
|
|
146
|
+
</dl>
|
|
147
|
+
</script>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
4
|
+
const { Client } = require('esphome-native-api');
|
|
5
|
+
module.exports = (RED) => {
|
|
6
|
+
RED.nodes.registerType('esphome-device', function (config) {
|
|
7
|
+
var _a;
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
9
|
+
const self = this;
|
|
10
|
+
self.config = config;
|
|
11
|
+
RED.nodes.createNode(this, config);
|
|
12
|
+
self.device = {};
|
|
13
|
+
self.entities = [];
|
|
14
|
+
self.current_status = 'disconnected';
|
|
15
|
+
if (!(config === null || config === void 0 ? void 0 : config.host) || !(config === null || config === void 0 ? void 0 : config.port) || !((_a = self.credentials) === null || _a === void 0 ? void 0 : _a.password)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
self.onStatus = function (string) {
|
|
19
|
+
self.current_status = string;
|
|
20
|
+
self.emit('onStatus', string);
|
|
21
|
+
};
|
|
22
|
+
self.onState = function (object) {
|
|
23
|
+
self.emit('onState', object);
|
|
24
|
+
};
|
|
25
|
+
self.client = new Client({
|
|
26
|
+
host: config.host,
|
|
27
|
+
port: config.port,
|
|
28
|
+
password: self.credentials.password,
|
|
29
|
+
clientInfo: 'node-red',
|
|
30
|
+
initializeDeviceInfo: true,
|
|
31
|
+
initializeListEntities: true,
|
|
32
|
+
initializeSubscribeStates: false,
|
|
33
|
+
reconnect: true,
|
|
34
|
+
reconnectInterval: 15 * 1000,
|
|
35
|
+
pingInterval: 5 * 1000
|
|
36
|
+
});
|
|
37
|
+
try {
|
|
38
|
+
self.client.connect();
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
self.error(e.message);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
self.client.on('error', (e) => {
|
|
45
|
+
if (e.message.includes('EHOSTUNREACH')) {
|
|
46
|
+
/* empty */
|
|
47
|
+
}
|
|
48
|
+
else if (e.message.includes('Invalid password')) {
|
|
49
|
+
self.error(e.message);
|
|
50
|
+
}
|
|
51
|
+
else if (e.message.includes('ECONNRESET')) {
|
|
52
|
+
/* empty */
|
|
53
|
+
}
|
|
54
|
+
else if (e.message.includes('timeout')) {
|
|
55
|
+
/* empty */
|
|
56
|
+
}
|
|
57
|
+
else if (e.message.includes('write after end')) {
|
|
58
|
+
/* empty */
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// copy this error to issues ...
|
|
62
|
+
}
|
|
63
|
+
self.onStatus('error');
|
|
64
|
+
});
|
|
65
|
+
self.client.on('disconnected', () => {
|
|
66
|
+
self.onStatus('disconnected');
|
|
67
|
+
});
|
|
68
|
+
self.client.on('connected', () => {
|
|
69
|
+
// clear entities
|
|
70
|
+
self.entities = [];
|
|
71
|
+
self.onStatus('connecting');
|
|
72
|
+
});
|
|
73
|
+
self.client.on('initialized', () => {
|
|
74
|
+
self.onStatus('connected');
|
|
75
|
+
});
|
|
76
|
+
self.client.on('deviceInfo', (deviceInfo) => {
|
|
77
|
+
self.device = deviceInfo;
|
|
78
|
+
});
|
|
79
|
+
self.client.on('newEntity', (entity) => {
|
|
80
|
+
self.entities.push({
|
|
81
|
+
key: entity.id,
|
|
82
|
+
type: entity.type,
|
|
83
|
+
name: entity.name,
|
|
84
|
+
config: entity.config
|
|
85
|
+
});
|
|
86
|
+
entity.connection.subscribeStatesService();
|
|
87
|
+
entity.on('state', (state) => {
|
|
88
|
+
self.onState(Object.assign({}, state));
|
|
89
|
+
});
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
91
|
+
entity.on('error', (e) => {
|
|
92
|
+
/* empty */
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
self.on('close', () => {
|
|
96
|
+
self.client.disconnect();
|
|
97
|
+
});
|
|
98
|
+
}, {
|
|
99
|
+
credentials: {
|
|
100
|
+
password: { type: 'password' }
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/nodes/device.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 73"><g fill="none" fill-rule="evenodd"><path d="M72.63 16.18V12c0-1.47 1.19-2.66 2.66-2.66 1.47 0 2.66 1.19 2.66 2.66v4.18m-14.27 0V12c0-1.47 1.19-2.66 2.66-2.66A2.65 2.65 0 0 1 69 11.99v4.18m-14.28.01V12c0-1.47 1.19-2.66 2.66-2.66 1.47 0 2.66 1.19 2.66 2.66v4.18m-14.28 0V12c0-1.47 1.19-2.66 2.66-2.66 1.47 0 2.66 1.19 2.66 2.66v4.18m-14.28 0V12c0-1.47 1.19-2.66 2.66-2.66 1.47 0 2.66 1.19 2.66 2.66v4.18m-14.27 0V12c0-1.47 1.19-2.66 2.66-2.66 1.47 0 2.66 1.19 2.66 2.66v4.18M77.95 56.07v4.68c0 1.47-1.19 2.66-2.66 2.66-1.47 0-2.66-1.19-2.66-2.66v-4.68m-3.63 0v4.68c0 1.47-1.19 2.66-2.66 2.66-1.47 0-2.66-1.19-2.66-2.66v-4.68m-3.64 0v4.68c0 1.47-1.19 2.66-2.66 2.66-1.47 0-2.66-1.19-2.66-2.66v-4.68m-3.64 0v4.68c0 1.47-1.19 2.66-2.66 2.66-1.47 0-2.66-1.19-2.66-2.66v-4.68m-3.64 0v4.68c0 1.47-1.19 2.66-2.66 2.66-1.47 0-2.66-1.19-2.66-2.66v-4.68m-3.63.26v4.43c0 1.47-1.19 2.66-2.66 2.66-1.47 0-2.66-1.19-2.66-2.66v-4.43" fill="#fff" stroke="#000" stroke-width="2.178"/><path fill="#fff" stroke="#000" stroke-width="2.42" d="M79.29 16.18H26.51a.97.97 0 0 0-.97.97v37.96c0 .53.43.97.97.97h52.77c.53 0 .97-.43.97-.97V17.15c0-.54-.43-.97-.96-.97z"/><path fill="#000" fill-rule="nonzero" d="M61.6 35.42v-5.07h-1.8v3.28l-6.81-6.81-11.49 11.5h2.87v7.75h17.25v-7.75h2.87z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.961" d="M61.6 35.42v-5.07h-1.8v3.28l-6.81-6.81-11.49 11.5h2.87v7.75h17.25v-7.75h2.87z"/><path d="M25.34 53.77H9.52v-3.89h11.86v-3.89H9.52v-3.88h11.86v-3.89H9.52v-3.89h11.86v-3.88H9.52V18.47" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.159"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|