iobroker.hassemu 1.2.0 → 1.3.0
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/README.md +45 -40
- package/admin/jsonConfig.json +159 -159
- package/build/lib/client-registry.js +6 -7
- package/build/lib/client-registry.js.map +2 -2
- package/build/lib/coerce.js +18 -7
- package/build/lib/coerce.js.map +2 -2
- package/build/lib/constants.js +10 -1
- package/build/lib/constants.js.map +2 -2
- package/build/lib/global-config.js +6 -15
- package/build/lib/global-config.js.map +2 -2
- package/build/lib/network.js +2 -7
- package/build/lib/network.js.map +2 -2
- package/build/lib/webserver.js +104 -20
- package/build/lib/webserver.js.map +2 -2
- package/io-package.json +25 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,16 +36,16 @@ ioBroker adapter that emulates a [Home Assistant](https://www.home-assistant.io)
|
|
|
36
36
|
- **Node.js >= 20**
|
|
37
37
|
- **ioBroker js-controller >= 7.0.7**
|
|
38
38
|
- **ioBroker Admin >= 7.7.22**
|
|
39
|
-
- **ioBroker web >= 8.0.0**
|
|
39
|
+
- **ioBroker web >= 8.0.0**
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
43
|
## Ports
|
|
44
44
|
|
|
45
|
-
| Port | Protocol | Purpose
|
|
46
|
-
| ---- | -------- |
|
|
47
|
-
| 8123 | TCP/HTTP | Home Assistant emulation (HA standard port)
|
|
48
|
-
| 5353 | UDP | mDNS service broadcast (only if mDNS enabled)| No |
|
|
45
|
+
| Port | Protocol | Purpose | Configurable |
|
|
46
|
+
| ---- | -------- | --------------------------------------------- | ------------ |
|
|
47
|
+
| 8123 | TCP/HTTP | Home Assistant emulation (HA standard port) | No — fixed |
|
|
48
|
+
| 5353 | UDP | mDNS service broadcast (only if mDNS enabled) | No |
|
|
49
49
|
|
|
50
50
|
---
|
|
51
51
|
|
|
@@ -53,15 +53,13 @@ ioBroker adapter that emulates a [Home Assistant](https://www.home-assistant.io)
|
|
|
53
53
|
|
|
54
54
|
The Admin UI configures the server. Redirect URLs are set via the state tree (see below).
|
|
55
55
|
|
|
56
|
-
| Option
|
|
57
|
-
|
|
58
|
-
| **Bind to Interface**
|
|
59
|
-
| **Service Name**
|
|
60
|
-
| **mDNS Enabled**
|
|
61
|
-
| **Auth Required**
|
|
62
|
-
| **Username / Password** | Used when
|
|
63
|
-
|
|
64
|
-
> URLs you set must be reachable from the display — use the LAN IP of the ioBroker host, not `localhost`.
|
|
56
|
+
| Option | Description | Default |
|
|
57
|
+
| ----------------------- | ---------------------------------------------------------------- | ------------- |
|
|
58
|
+
| **Bind to Interface** | Network interface to listen on | 0.0.0.0 (all) |
|
|
59
|
+
| **Service Name** | Name broadcast via mDNS, shown as the server name on the display | `ioBroker` |
|
|
60
|
+
| **mDNS Enabled** | Broadcast `_home-assistant._tcp` on the LAN | `true` |
|
|
61
|
+
| **Auth Required** | Check credentials the display sends during login | `false` |
|
|
62
|
+
| **Username / Password** | Used when _Auth Required_ is on (password encrypted at rest) | `admin` / — |
|
|
65
63
|
|
|
66
64
|
---
|
|
67
65
|
|
|
@@ -86,11 +84,11 @@ hassemu.0.
|
|
|
86
84
|
|
|
87
85
|
The adapter reads `clients.<id>.mode` on every visit:
|
|
88
86
|
|
|
89
|
-
| `mode` value
|
|
90
|
-
|
|
91
|
-
| `global`
|
|
92
|
-
| `manual`
|
|
93
|
-
| a URL
|
|
87
|
+
| `mode` value | redirect target |
|
|
88
|
+
| --------------- | -------------------------------------------------------------- |
|
|
89
|
+
| `global` | `global.mode` / `global.manualUrl` (same rules, one level up) |
|
|
90
|
+
| `manual` | `clients.<id>.manualUrl` |
|
|
91
|
+
| a URL | that URL |
|
|
94
92
|
| empty / unknown | landing page (small HTML with device ID, refreshes every 15 s) |
|
|
95
93
|
|
|
96
94
|
### Master switch
|
|
@@ -107,12 +105,12 @@ The adapter broadcasts `_home-assistant._tcp` via mDNS. If the display does not
|
|
|
107
105
|
|
|
108
106
|
1. Check the adapter log for `mDNS: Broadcasting`.
|
|
109
107
|
2. Verify the service is visible from another host:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
```bash
|
|
109
|
+
# macOS
|
|
110
|
+
dns-sd -B _home-assistant._tcp
|
|
111
|
+
# Linux (with avahi-utils)
|
|
112
|
+
avahi-browse _home-assistant._tcp -r -t
|
|
113
|
+
```
|
|
116
114
|
3. Make sure UDP 5353 is not blocked by a firewall.
|
|
117
115
|
4. If mDNS is not usable on your LAN, set the URL manually on the display: `http://<ioBroker-IP>:8123`.
|
|
118
116
|
|
|
@@ -149,23 +147,33 @@ Reverse DNS on a home LAN depends on your router/DHCP server and often fails. Th
|
|
|
149
147
|
---
|
|
150
148
|
|
|
151
149
|
## Changelog
|
|
150
|
+
### 1.3.0 (2026-04-30)
|
|
151
|
+
|
|
152
|
+
- Security: brute-force lockout on `/auth/login_flow/:flowId` — after 5 failed credential attempts an IP is rejected with HTTP 429 for 15 min. Successful login resets the counter.
|
|
153
|
+
- DRY refactor: shared `parseManualUrlWrite` helper between client + global config; FIFO-cap helper in WebServer; OAuth access-token TTL + lockout window/threshold are now named constants instead of magic numbers.
|
|
154
|
+
- Dead-code cleanup: `resolveBindToReachable`, `coerceUuid` strict-V4 parameter, `DEFAULT_REFRESH_DEBOUNCE_MS` export, internal `getMode`/`getManualUrl` test affordances — all removed; tests rewritten to assert observable behaviour.
|
|
155
|
+
- New `landing-page` test suite with XSS-escape coverage and 11-language fallback verification.
|
|
156
|
+
- Emulated Home Assistant version bumped from 2026.3.1 to 2026.4.0.
|
|
157
|
+
|
|
152
158
|
### 1.2.0 (2026-04-29)
|
|
153
159
|
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
160
|
+
- Redirect target now configured via `mode` (dropdown) + `manualUrl` (free text) instead of the old `visUrl`. Migration runs automatically.
|
|
161
|
+
- Master switch `global.enabled` syncs every display: on → all follow the global URL, off → each display picks up its own again.
|
|
162
|
+
- Idle displays without auth token are auto-removed after 30 days.
|
|
163
|
+
- Security hardening of the auth flow.
|
|
164
|
+
- `web` adapter declared as dependency.
|
|
159
165
|
|
|
160
166
|
### 1.1.6 (2026-04-28)
|
|
167
|
+
|
|
161
168
|
- Audit cleanup against the upstream `ioBroker.example/TypeScript` full standard:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
- Test setup migrated: tests now live next to source as `src/lib/*.test.ts` and run directly via `ts-node/register`. Removed `tsconfig.test.json` + `build-test/`, added `test/mocharc.custom.json` + `test/mocha.setup.js` + `test/tsconfig.json` + `test/.eslintrc.json`
|
|
170
|
+
- `@types/node` rolled back from `^25.6.0` to `^20.19.24` so type defs match `engines.node: ">=20"`
|
|
171
|
+
- Dependabot now ignores major bumps for `@types/node`, `typescript`, `eslint`, `actions/checkout`, `actions/setup-node`
|
|
172
|
+
- `nyc` config + `coverage` script added
|
|
173
|
+
- Orphan `.github/auto-merge.yml` removed (active workflow is `automerge-dependabot.yml` using `gh pr merge`)
|
|
167
174
|
|
|
168
175
|
### 1.1.5 (2026-04-26)
|
|
176
|
+
|
|
169
177
|
- Process-level `unhandledRejection` / `uncaughtException` handlers added as last-line-of-defence against fire-and-forget rejections.
|
|
170
178
|
- Stop shipping the `manual-review` release-script plugin — adapter-only consequence.
|
|
171
179
|
- Audit-driven boilerplate sync with the other krobi adapters (`.vscode` json5 schemas, `tsconfig.test` looser test rules).
|
|
@@ -173,13 +181,10 @@ Reverse DNS on a home LAN depends on your router/DHCP server and often fails. Th
|
|
|
173
181
|
- `@types/iobroker` bumped to `^7.1.1`.
|
|
174
182
|
|
|
175
183
|
### 1.1.4 (2026-04-23)
|
|
176
|
-
- Separate test-build output (`build-test/`) from production `build/` — `npm test` no longer risks leaving duplicated `build/src` + `build/test` trees in the published package. No runtime change.
|
|
177
184
|
|
|
178
|
-
|
|
185
|
+
- Separate test-build output (`build-test/`) from production `build/` — `npm test` no longer risks leaving duplicated `build/src` + `build/test` trees in the published package. No runtime change.
|
|
179
186
|
|
|
180
|
-
|
|
181
|
-
- **Setup page redesigned** — big green OK banner so "everything's connected" is visible at a glance, responsive layout with dark-mode support, IP shown alongside the device ID, clearer step-by-step instructions.
|
|
182
|
-
- **Setup page localized into all 11 adapter languages** — automatically picks the ioBroker system language (set in Admin → Main Settings), falls back to English for unknown languages.
|
|
187
|
+
Older entries are in [CHANGELOG_OLD.md](CHANGELOG_OLD.md).
|
|
183
188
|
|
|
184
189
|
## Support
|
|
185
190
|
|
package/admin/jsonConfig.json
CHANGED
|
@@ -1,161 +1,161 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
2
|
+
"i18n": true,
|
|
3
|
+
"type": "panel",
|
|
4
|
+
"items": {
|
|
5
|
+
"_headerServer": {
|
|
6
|
+
"type": "header",
|
|
7
|
+
"text": "header_server",
|
|
8
|
+
"size": 4
|
|
9
|
+
},
|
|
10
|
+
"bindAddress": {
|
|
11
|
+
"type": "ip",
|
|
12
|
+
"label": "bindAddress",
|
|
13
|
+
"tooltip": "bindAddressTooltip",
|
|
14
|
+
"listenOnAllPorts": true,
|
|
15
|
+
"onlyIp4": true,
|
|
16
|
+
"noInternal": false,
|
|
17
|
+
"xs": 12,
|
|
18
|
+
"sm": 6,
|
|
19
|
+
"md": 4,
|
|
20
|
+
"lg": 4,
|
|
21
|
+
"xl": 4
|
|
22
|
+
},
|
|
23
|
+
"serviceName": {
|
|
24
|
+
"type": "text",
|
|
25
|
+
"label": "serviceName",
|
|
26
|
+
"tooltip": "serviceNameTooltip",
|
|
27
|
+
"default": "ioBroker",
|
|
28
|
+
"xs": 12,
|
|
29
|
+
"sm": 6,
|
|
30
|
+
"md": 4,
|
|
31
|
+
"lg": 4,
|
|
32
|
+
"xl": 4
|
|
33
|
+
},
|
|
34
|
+
"_redirectInfo": {
|
|
35
|
+
"type": "staticText",
|
|
36
|
+
"text": "redirectInfo",
|
|
37
|
+
"xs": 12,
|
|
38
|
+
"sm": 12,
|
|
39
|
+
"md": 12,
|
|
40
|
+
"lg": 12,
|
|
41
|
+
"xl": 12
|
|
42
|
+
},
|
|
43
|
+
"_headerMdns": {
|
|
44
|
+
"type": "header",
|
|
45
|
+
"text": "header_mdns",
|
|
46
|
+
"size": 4
|
|
47
|
+
},
|
|
48
|
+
"mdnsEnabled": {
|
|
49
|
+
"type": "checkbox",
|
|
50
|
+
"label": "mdnsEnabled",
|
|
51
|
+
"tooltip": "mdnsEnabledTooltip",
|
|
52
|
+
"default": true,
|
|
53
|
+
"xs": 12,
|
|
54
|
+
"sm": 12,
|
|
55
|
+
"md": 12,
|
|
56
|
+
"lg": 12,
|
|
57
|
+
"xl": 12
|
|
58
|
+
},
|
|
59
|
+
"_mdnsInfo": {
|
|
60
|
+
"type": "staticText",
|
|
61
|
+
"text": "mdnsInfo",
|
|
62
|
+
"xs": 12,
|
|
63
|
+
"sm": 12,
|
|
64
|
+
"md": 12,
|
|
65
|
+
"lg": 12,
|
|
66
|
+
"xl": 12
|
|
67
|
+
},
|
|
68
|
+
"_headerAuth": {
|
|
69
|
+
"type": "header",
|
|
70
|
+
"text": "header_auth",
|
|
71
|
+
"size": 4
|
|
72
|
+
},
|
|
73
|
+
"authRequired": {
|
|
74
|
+
"type": "checkbox",
|
|
75
|
+
"label": "authRequired",
|
|
76
|
+
"tooltip": "authRequiredTooltip",
|
|
77
|
+
"default": false,
|
|
78
|
+
"xs": 12,
|
|
79
|
+
"sm": 12,
|
|
80
|
+
"md": 12,
|
|
81
|
+
"lg": 12,
|
|
82
|
+
"xl": 12
|
|
83
|
+
},
|
|
84
|
+
"username": {
|
|
85
|
+
"type": "text",
|
|
86
|
+
"label": "username",
|
|
87
|
+
"default": "admin",
|
|
88
|
+
"hidden": "!data.authRequired",
|
|
89
|
+
"xs": 12,
|
|
90
|
+
"sm": 6,
|
|
91
|
+
"md": 6,
|
|
92
|
+
"lg": 6,
|
|
93
|
+
"xl": 6
|
|
94
|
+
},
|
|
95
|
+
"password": {
|
|
96
|
+
"type": "password",
|
|
97
|
+
"label": "password",
|
|
98
|
+
"visible": true,
|
|
99
|
+
"hidden": "!data.authRequired",
|
|
100
|
+
"xs": 12,
|
|
101
|
+
"sm": 6,
|
|
102
|
+
"md": 6,
|
|
103
|
+
"lg": 6,
|
|
104
|
+
"xl": 6
|
|
105
|
+
},
|
|
106
|
+
"_authInfo": {
|
|
107
|
+
"type": "staticText",
|
|
108
|
+
"text": "authInfo",
|
|
109
|
+
"hidden": "data.authRequired",
|
|
110
|
+
"xs": 12,
|
|
111
|
+
"sm": 12,
|
|
112
|
+
"md": 12,
|
|
113
|
+
"lg": 12,
|
|
114
|
+
"xl": 12
|
|
115
|
+
},
|
|
116
|
+
"_supportHeader": {
|
|
117
|
+
"type": "header",
|
|
118
|
+
"text": "supportHeader",
|
|
119
|
+
"size": 5
|
|
120
|
+
},
|
|
121
|
+
"_aboutInfo": {
|
|
122
|
+
"type": "staticText",
|
|
123
|
+
"text": "aboutInfo",
|
|
124
|
+
"xs": 12,
|
|
125
|
+
"sm": 12,
|
|
126
|
+
"md": 12,
|
|
127
|
+
"lg": 12,
|
|
128
|
+
"xl": 12,
|
|
129
|
+
"style": {
|
|
130
|
+
"fontSize": 14,
|
|
131
|
+
"marginBottom": 16
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"_kofiLink": {
|
|
135
|
+
"type": "staticLink",
|
|
136
|
+
"href": "https://ko-fi.com/krobipd",
|
|
137
|
+
"label": "donateKofi",
|
|
138
|
+
"button": true,
|
|
139
|
+
"variant": "outlined",
|
|
140
|
+
"color": "primary",
|
|
141
|
+
"xs": 12,
|
|
142
|
+
"sm": 6,
|
|
143
|
+
"md": 4,
|
|
144
|
+
"lg": 4,
|
|
145
|
+
"xl": 4
|
|
146
|
+
},
|
|
147
|
+
"_paypalLink": {
|
|
148
|
+
"type": "staticLink",
|
|
149
|
+
"href": "https://paypal.me/krobipd",
|
|
150
|
+
"label": "donatePaypal",
|
|
151
|
+
"button": true,
|
|
152
|
+
"variant": "outlined",
|
|
153
|
+
"color": "primary",
|
|
154
|
+
"xs": 12,
|
|
155
|
+
"sm": 6,
|
|
156
|
+
"md": 4,
|
|
157
|
+
"lg": 4,
|
|
158
|
+
"xl": 4
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
161
|
}
|
|
@@ -251,21 +251,20 @@ class ClientRegistry {
|
|
|
251
251
|
* @param rawValue Value written to the state.
|
|
252
252
|
*/
|
|
253
253
|
async handleManualUrlWrite(id, rawValue) {
|
|
254
|
-
var _a;
|
|
254
|
+
var _a, _b;
|
|
255
255
|
const record = this.byId.get(id);
|
|
256
256
|
if (!record) {
|
|
257
257
|
return;
|
|
258
258
|
}
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
if (!empty && !safe) {
|
|
259
|
+
const result = (0, import_coerce.parseManualUrlWrite)(rawValue);
|
|
260
|
+
if (!result.ok) {
|
|
262
261
|
this.adapter.log.warn(`client-registry: rejected unsafe manualUrl for ${id}`);
|
|
263
262
|
await this.adapter.setStateAsync(`clients.${id}.manualUrl`, { val: (_a = record.manualUrl) != null ? _a : "", ack: true });
|
|
264
263
|
return;
|
|
265
264
|
}
|
|
266
|
-
record.manualUrl = safe;
|
|
267
|
-
await this.adapter.setStateAsync(`clients.${id}.manualUrl`, { val: safe != null ?
|
|
268
|
-
if (record.mode === import_global_config.MODE_MANUAL && !safe) {
|
|
265
|
+
record.manualUrl = result.safe;
|
|
266
|
+
await this.adapter.setStateAsync(`clients.${id}.manualUrl`, { val: (_b = result.safe) != null ? _b : "", ack: true });
|
|
267
|
+
if (record.mode === import_global_config.MODE_MANUAL && !result.safe) {
|
|
269
268
|
this.adapter.log.warn(
|
|
270
269
|
`client-registry: ${id} manualUrl cleared while mode='manual' \u2014 display will hit the setup page`
|
|
271
270
|
);
|