mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-29 15:18:41 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b485a98c5 | ||
|
|
62ab814d73 | ||
|
|
fb3189b743 | ||
|
|
73d8ae8956 | ||
|
|
969db9ae85 | ||
|
|
66985a8ce8 | ||
|
|
52b063bb32 | ||
|
|
3f2c00cdb7 | ||
|
|
dde637af2f | ||
|
|
867179c430 | ||
|
|
2e5e362818 | ||
|
|
64286ebb30 | ||
|
|
6fb9d563e6 | ||
|
|
e054d7124b | ||
|
|
35567cb67f | ||
|
|
075f87c782 | ||
|
|
9f49d0ec67 | ||
|
|
67aa5099df | ||
|
|
69641d5467 | ||
|
|
6fca3744c9 | ||
|
|
f60db2d7a6 | ||
|
|
08bf2cbc07 | ||
|
|
c790e7b68f | ||
|
|
a8633760bc | ||
|
|
ff01c8718d | ||
|
|
cd6977bce6 | ||
|
|
9143924482 | ||
|
|
0f781ee9c0 | ||
|
|
9f1eb764a6 | ||
|
|
b7f1cecb49 | ||
|
|
05946b5955 | ||
|
|
4a8369295c | ||
|
|
1747618afa | ||
|
|
284d86e93b | ||
|
|
d6323825e4 | ||
|
|
b7b43030eb | ||
|
|
aefbb8be14 | ||
|
|
fc3a1968f6 | ||
|
|
c56fbca457 |
@@ -31,7 +31,7 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
if: ((github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push') == false
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@v5
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@@ -63,14 +63,14 @@ jobs:
|
||||
|
||||
- name: Log in to the Docker container registry
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: koenkk
|
||||
password: ${{ secrets.DOCKER_KEY }}
|
||||
|
||||
- name: Log in to the GitHub container registry
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: koenkk
|
||||
@@ -78,20 +78,20 @@ jobs:
|
||||
|
||||
- name: Docker setup - QEMU
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v4
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
- name: Docker setup - Buildx
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: dev - Docker build and push
|
||||
if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
|
||||
- name: release - Docker meta
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
id: meta
|
||||
with:
|
||||
images: |
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
|
||||
- name: release - Docker build and push
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
@@ -222,7 +222,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@v5
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
version: '${{steps.release.outputs.major}}.${{steps.release.outputs.minor}}.${{steps.release.outputs.patch}}'
|
||||
steps:
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@v5
|
||||
with:
|
||||
version: 9
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
with:
|
||||
ref: dev
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "2.9.1"
|
||||
".": "2.9.2"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
# Changelog
|
||||
|
||||
## [2.9.2](https://github.com/Koenkk/zigbee2mqtt/compare/2.9.1...2.9.2) (2026-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Export definition version ([#31270](https://github.com/Koenkk/zigbee2mqtt/issues/31270)) ([9f1eb76](https://github.com/Koenkk/zigbee2mqtt/commit/9f1eb764a62b041a7d9782066a8ffcaa3562eaad))
|
||||
* Home Assistant: fix entity names for derived weather sensors by removing `device_class` ([#31234](https://github.com/Koenkk/zigbee2mqtt/issues/31234)) ([b7b4303](https://github.com/Koenkk/zigbee2mqtt/commit/b7b43030eb8aaa144dbb5477805638d860efdd38))
|
||||
* **ignore:** bump @biomejs/biome from 2.4.4 to 2.4.5 in the minor-patch group ([#31237](https://github.com/Koenkk/zigbee2mqtt/issues/31237)) ([aefbb8b](https://github.com/Koenkk/zigbee2mqtt/commit/aefbb8be14ae5d84495278cbe9a6ca9d750553cb))
|
||||
* **ignore:** bump the minor-patch group with 2 updates ([#31411](https://github.com/Koenkk/zigbee2mqtt/issues/31411)) ([67aa509](https://github.com/Koenkk/zigbee2mqtt/commit/67aa5099df2371eb9ee220be7a8821f0d4f8b38b))
|
||||
* **ignore:** bump the minor-patch group with 3 updates ([#31295](https://github.com/Koenkk/zigbee2mqtt/issues/31295)) ([b7f1cec](https://github.com/Koenkk/zigbee2mqtt/commit/b7f1cecb49f088b7fc79dcef068034ca1d012c1d))
|
||||
* **ignore:** bump the minor-patch group with 3 updates ([#31489](https://github.com/Koenkk/zigbee2mqtt/issues/31489)) ([52b063b](https://github.com/Koenkk/zigbee2mqtt/commit/52b063bb32429e14b30319c905e596b7632bd674))
|
||||
* **ignore:** bump the minor-patch group with 3 updates ([#31559](https://github.com/Koenkk/zigbee2mqtt/issues/31559)) ([73d8ae8](https://github.com/Koenkk/zigbee2mqtt/commit/73d8ae895613af0d77f0a684f7305d688af11146))
|
||||
* **ignore:** update zigbee-herdsman to 10.0.1 ([#31383](https://github.com/Koenkk/zigbee2mqtt/issues/31383)) ([6fca374](https://github.com/Koenkk/zigbee2mqtt/commit/6fca3744c9715dd4afb7fc777cf783c84bfb91fc))
|
||||
* **ignore:** update zigbee-herdsman to 10.0.2 ([#31413](https://github.com/Koenkk/zigbee2mqtt/issues/31413)) ([075f87c](https://github.com/Koenkk/zigbee2mqtt/commit/075f87c7822c29c74fc1c4031866aca8c445c9be))
|
||||
* **ignore:** update zigbee-herdsman to 10.0.3 ([#31450](https://github.com/Koenkk/zigbee2mqtt/issues/31450)) ([6fb9d56](https://github.com/Koenkk/zigbee2mqtt/commit/6fb9d563e69c2db3b31fd0f163e5f30924047e3f))
|
||||
* **ignore:** update zigbee-herdsman to 10.0.4 ([#31478](https://github.com/Koenkk/zigbee2mqtt/issues/31478)) ([867179c](https://github.com/Koenkk/zigbee2mqtt/commit/867179c430eacf9bd66ef359b9a7435f2299d944))
|
||||
* **ignore:** update zigbee-herdsman to 10.0.5 ([#31562](https://github.com/Koenkk/zigbee2mqtt/issues/31562)) ([fb3189b](https://github.com/Koenkk/zigbee2mqtt/commit/fb3189b7431cb203092bbe235bd35194e2b44a44))
|
||||
* **ignore:** update zigbee-herdsman to 9.0.10 ([#31245](https://github.com/Koenkk/zigbee2mqtt/issues/31245)) ([d632382](https://github.com/Koenkk/zigbee2mqtt/commit/d6323825e4ae3168d52a096e7647bc7174d3cdb1))
|
||||
* **ignore:** update zigbee-herdsman to 9.0.11 ([#31257](https://github.com/Koenkk/zigbee2mqtt/issues/31257)) ([1747618](https://github.com/Koenkk/zigbee2mqtt/commit/1747618afa3948ac9fb8899d94fba8456198e4b5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.13.0 ([#31231](https://github.com/Koenkk/zigbee2mqtt/issues/31231)) ([fc3a196](https://github.com/Koenkk/zigbee2mqtt/commit/fc3a1968f6635d246ad2d68e5c6b79d495fd96bb))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.14.0 ([#31246](https://github.com/Koenkk/zigbee2mqtt/issues/31246)) ([284d86e](https://github.com/Koenkk/zigbee2mqtt/commit/284d86e93ba8cd83139d6dd0217519f67c01e565))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.16.1 ([#31268](https://github.com/Koenkk/zigbee2mqtt/issues/31268)) ([05946b5](https://github.com/Koenkk/zigbee2mqtt/commit/05946b59552608c6ae8a8b02098552fe611f027a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.17.0 ([#31314](https://github.com/Koenkk/zigbee2mqtt/issues/31314)) ([9143924](https://github.com/Koenkk/zigbee2mqtt/commit/91439244823d9897998a57448321d2e8af9b083f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.18.0 ([#31362](https://github.com/Koenkk/zigbee2mqtt/issues/31362)) ([f60db2d](https://github.com/Koenkk/zigbee2mqtt/commit/f60db2d7a641ab5fccbb3a7b6a0e1e73983559ef))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.19.1 ([#31384](https://github.com/Koenkk/zigbee2mqtt/issues/31384)) ([69641d5](https://github.com/Koenkk/zigbee2mqtt/commit/69641d5467ec5e3ddfebb4ff98142d0ff990c814))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.20.0 ([#31415](https://github.com/Koenkk/zigbee2mqtt/issues/31415)) ([9f49d0e](https://github.com/Koenkk/zigbee2mqtt/commit/9f49d0ec6738ebd1ea65a3bf1aae553548217f2e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.21.0 ([#31427](https://github.com/Koenkk/zigbee2mqtt/issues/31427)) ([e054d71](https://github.com/Koenkk/zigbee2mqtt/commit/e054d7124bd4dc5bdb18c9de4a6923b06df6b929))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.22.0 ([#31451](https://github.com/Koenkk/zigbee2mqtt/issues/31451)) ([64286eb](https://github.com/Koenkk/zigbee2mqtt/commit/64286ebb3054bc15e020aab431c1d3af76e2c914))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.23.0 ([#31462](https://github.com/Koenkk/zigbee2mqtt/issues/31462)) ([2e5e362](https://github.com/Koenkk/zigbee2mqtt/commit/2e5e3628188765cddbd4116f41703eab491670df))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.24.0 ([#31479](https://github.com/Koenkk/zigbee2mqtt/issues/31479)) ([dde637a](https://github.com/Koenkk/zigbee2mqtt/commit/dde637af2f8449ae24684fa34a32bec106c24913))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.25.0 ([#31497](https://github.com/Koenkk/zigbee2mqtt/issues/31497)) ([66985a8](https://github.com/Koenkk/zigbee2mqtt/commit/66985a8ce8d4cefbc4ddacc754e182e0b5953cea))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.26.0 ([#31510](https://github.com/Koenkk/zigbee2mqtt/issues/31510)) ([969db9a](https://github.com/Koenkk/zigbee2mqtt/commit/969db9ae85838b955e07a80f5f72f655f6beffcf))
|
||||
* **ignore:** update zigbee-herdsman-converters to 26.27.0 ([#31563](https://github.com/Koenkk/zigbee2mqtt/issues/31563)) ([62ab814](https://github.com/Koenkk/zigbee2mqtt/commit/62ab814d73b7de957fb8b28679b9dea8ae087133))
|
||||
* Match new cluster-related typing from ZH ([#31298](https://github.com/Koenkk/zigbee2mqtt/issues/31298)) ([0f781ee](https://github.com/Koenkk/zigbee2mqtt/commit/0f781ee9c03ae502745841f4dd7d6f3775c620d4))
|
||||
* Reintroduce onboarding improvements ([#31273](https://github.com/Koenkk/zigbee2mqtt/issues/31273)) ([4a83692](https://github.com/Koenkk/zigbee2mqtt/commit/4a8369295c5d6c07ff1ac66f90a35b0e8b915296))
|
||||
|
||||
## [2.9.1](https://github.com/Koenkk/zigbee2mqtt/compare/2.9.0...2.9.1) (2026-03-02)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.4.1/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
|
||||
@@ -900,6 +900,7 @@ export default class Bridge extends Extension {
|
||||
exposes: device.exposes(),
|
||||
supports_ota: !!device.definition.ota,
|
||||
options: device.definition.options ?? [],
|
||||
version: device.definition.version,
|
||||
icon,
|
||||
};
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export default class Configure extends Extension {
|
||||
|
||||
if (!device || !(device instanceof Device)) {
|
||||
error = `Device '${ID}' does not exist`;
|
||||
} else if (!device.definition || !device.definition.configure) {
|
||||
} else if (!device.definition?.configure) {
|
||||
error = `Device '${device.name}' cannot be configured`;
|
||||
} else {
|
||||
try {
|
||||
|
||||
@@ -168,7 +168,7 @@ export default class Groups extends Extension {
|
||||
endpointNames &&
|
||||
endpointNames.length >= member.ID &&
|
||||
device.definition?.meta?.multiEndpoint &&
|
||||
(!device.definition.meta.multiEndpointSkip || !device.definition.meta.multiEndpointSkip.includes("state"))
|
||||
!device.definition.meta.multiEndpointSkip?.includes("state")
|
||||
? `state_${endpointNames[member.ID - 1]}`
|
||||
: "state";
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ const NUMERIC_DISCOVERY_LOOKUP: {[s: string]: KeyValue} = {
|
||||
alarm_temperature_min: {device_class: "temperature", entity_category: "config", icon: "mdi:thermometer-low"},
|
||||
angle: {icon: "angle-acute"},
|
||||
angle_axis: {icon: "angle-acute"},
|
||||
apparent_temperature: {device_class: "temperature", icon: "mdi:thermometer-lines", state_class: "measurement"},
|
||||
apparent_temperature: {icon: "mdi:thermometer-lines", state_class: "measurement"},
|
||||
aqi: {device_class: "aqi", state_class: "measurement"},
|
||||
auto_relock_time: {entity_category: "config", icon: "mdi:timer"},
|
||||
away_preset_days: {entity_category: "config", icon: "mdi:timer"},
|
||||
@@ -154,7 +154,7 @@ const NUMERIC_DISCOVERY_LOOKUP: {[s: string]: KeyValue} = {
|
||||
entity_category: "diagnostic",
|
||||
state_class: "measurement",
|
||||
},
|
||||
dew_point: {device_class: "temperature", icon: "mdi:thermometer-water", state_class: "measurement"},
|
||||
dew_point: {icon: "mdi:thermometer-water", state_class: "measurement"},
|
||||
distance: {device_class: "distance", state_class: "measurement"},
|
||||
duration: {entity_category: "config", icon: "mdi:timer"},
|
||||
eco2: {device_class: "volatile_organic_compounds_parts", state_class: "measurement"},
|
||||
@@ -167,10 +167,10 @@ const NUMERIC_DISCOVERY_LOOKUP: {[s: string]: KeyValue} = {
|
||||
flow: {device_class: "volume_flow_rate", state_class: "measurement"},
|
||||
gas: {device_class: "gas", state_class: "total_increasing", icon: "mdi:meter-gas"},
|
||||
gas_density: {icon: "mdi:google-circles-communities", state_class: "measurement"},
|
||||
gust_speed: {device_class: "wind_speed", icon: "mdi:weather-windy-variant", state_class: "measurement"},
|
||||
gust_speed: {icon: "mdi:weather-windy-variant", state_class: "measurement"},
|
||||
hcho: {icon: "mdi:air-filter", state_class: "measurement"},
|
||||
heat_stress: {icon: "mdi:weather-sunny-alert", state_class: "measurement"},
|
||||
humidex: {device_class: "temperature", icon: "mdi:thermometer-alert", state_class: "measurement"},
|
||||
humidex: {icon: "mdi:thermometer-alert", state_class: "measurement"},
|
||||
humidity: {device_class: "humidity", state_class: "measurement"},
|
||||
humidity_calibration: {entity_category: "config", icon: "mdi:wrench-clock"},
|
||||
humidity_max: {entity_category: "config", icon: "mdi:water-percent"},
|
||||
@@ -254,7 +254,7 @@ const NUMERIC_DISCOVERY_LOOKUP: {[s: string]: KeyValue} = {
|
||||
device_class: "water",
|
||||
state_class: "total_increasing",
|
||||
},
|
||||
wind_chill: {device_class: "temperature", icon: "mdi:snowflake-thermometer", state_class: "measurement"},
|
||||
wind_chill: {icon: "mdi:snowflake-thermometer", state_class: "measurement"},
|
||||
wind_direction: {icon: "mdi:compass-outline", state_class: "measurement"},
|
||||
wind_speed: {device_class: "wind_speed", icon: "mdi:weather-windy", state_class: "measurement"},
|
||||
x: {icon: "mdi:axis-x-arrow", state_class: "measurement"},
|
||||
@@ -1867,11 +1867,11 @@ export class HomeAssistant extends Extension {
|
||||
try {
|
||||
message = JSON.parse(data.message);
|
||||
const baseTopic = `${settings.get().mqtt.base_topic}/`;
|
||||
if (isDeviceAutomation && (!message.topic || !message.topic.startsWith(baseTopic))) {
|
||||
if (isDeviceAutomation && !message.topic?.startsWith(baseTopic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDeviceAutomation && (!message.availability || !message.availability[0].topic.startsWith(baseTopic))) {
|
||||
if (!isDeviceAutomation && !message.availability?.[0].topic.startsWith(baseTopic)) {
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -284,11 +284,11 @@ export default class OTAUpdate extends Extension {
|
||||
|
||||
if (payload.url) {
|
||||
source.url = payload.url;
|
||||
} else if (!device.definition || !device.definition.ota) {
|
||||
} else if (!device.definition?.ota) {
|
||||
error = `Device '${device.name}' does not support OTA updates`;
|
||||
break;
|
||||
}
|
||||
} else if (!device.definition || !device.definition.ota) {
|
||||
} else if (!device.definition?.ota) {
|
||||
error = `Device '${device.name}' does not support OTA updates`;
|
||||
break;
|
||||
}
|
||||
@@ -344,7 +344,7 @@ export default class OTAUpdate extends Extension {
|
||||
source.url = writeFirmwareHexToDataDir(payload.hex.data, payload.hex.file_name, device.ieeeAddr);
|
||||
} else if (payload.url) {
|
||||
source.url = payload.url;
|
||||
} else if (!device.definition || !device.definition.ota) {
|
||||
} else if (!device.definition?.ota) {
|
||||
error = `Device '${device.name}' does not support OTA updates`;
|
||||
break;
|
||||
}
|
||||
@@ -360,7 +360,7 @@ export default class OTAUpdate extends Extension {
|
||||
if (payload.default_maximum_data_size) {
|
||||
dataSettings.baseSize = payload.default_maximum_data_size;
|
||||
}
|
||||
} else if (!device.definition || !device.definition.ota) {
|
||||
} else if (!device.definition?.ota) {
|
||||
error = `Device '${device.name}' does not support OTA updates`;
|
||||
break;
|
||||
}
|
||||
@@ -415,11 +415,11 @@ export default class OTAUpdate extends Extension {
|
||||
source.url = writeFirmwareHexToDataDir(payload.hex.data, payload.hex.file_name, device.ieeeAddr);
|
||||
} else if (payload.url) {
|
||||
source.url = payload.url;
|
||||
} else if (!device.definition || !device.definition.ota) {
|
||||
} else if (!device.definition?.ota) {
|
||||
error = `Device '${device.name}' does not support OTA updates`;
|
||||
break;
|
||||
}
|
||||
} else if (!device.definition || !device.definition.ota) {
|
||||
} else if (!device.definition?.ota) {
|
||||
error = `Device '${device.name}' does not support OTA updates`;
|
||||
break;
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
import type * as zigbeeHerdsman from "zigbee-herdsman/dist";
|
||||
import type {ZclPayload} from "zigbee-herdsman/dist/adapter/events";
|
||||
import type {Eui64} from "zigbee-herdsman/dist/zspec/tstypes";
|
||||
import type {ClusterDefinition, ClusterName, CustomClusters} from "zigbee-herdsman/dist/zspec/zcl/definition/tstype";
|
||||
import type {Cluster, ClusterName, CustomClusters} from "zigbee-herdsman/dist/zspec/zcl/definition/tstype";
|
||||
import type {GenericZdoResponse, RoutingTableEntry} from "zigbee-herdsman/dist/zspec/zdo/definition/tstypes";
|
||||
import type * as zigbeeHerdsmanConverter from "zigbee-herdsman-converters";
|
||||
import type {Base} from "zigbee-herdsman-converters/lib/exposes";
|
||||
@@ -259,6 +259,7 @@ export interface Zigbee2MQTTDeviceDefinition {
|
||||
exposes: zigbeeHerdsmanConverter.Expose[];
|
||||
supports_ota: boolean;
|
||||
options: zigbeeHerdsmanConverter.Option[];
|
||||
version: `0.0.${number}`;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
@@ -343,7 +344,7 @@ export interface Zigbee2MQTTAPI {
|
||||
};
|
||||
|
||||
"bridge/definitions": {
|
||||
clusters: Readonly<Record<ClusterName, Readonly<ClusterDefinition>>>;
|
||||
clusters: Readonly<Record<ClusterName, Cluster>>;
|
||||
custom_clusters: Record<string, CustomClusters>;
|
||||
actions: string[];
|
||||
};
|
||||
|
||||
+187
-459
@@ -1,398 +1,98 @@
|
||||
import {existsSync, mkdirSync} from "node:fs";
|
||||
import {existsSync, mkdirSync, writeFileSync} from "node:fs";
|
||||
import type {ServerResponse} from "node:http";
|
||||
import {createServer} from "node:http";
|
||||
import {parse} from "node:querystring";
|
||||
import path from "node:path";
|
||||
import expressStaticGzip from "express-static-gzip";
|
||||
import finalhandler from "finalhandler";
|
||||
import stringify from "json-stable-stringify-without-jsonify";
|
||||
import JSZip from "jszip";
|
||||
import {findAllDevices} from "zigbee-herdsman/dist/adapter/adapterDiscovery";
|
||||
import type {OnboardData, OnboardFailureData, OnboardSubmitResponse, Zigbee2MQTTSettings} from "../types/api";
|
||||
import data from "./data";
|
||||
import * as settings from "./settings";
|
||||
import {YAMLFileException} from "./yaml";
|
||||
|
||||
type OnboardSettings = {
|
||||
mqtt_base_topic?: string;
|
||||
mqtt_server?: string;
|
||||
mqtt_user?: string;
|
||||
mqtt_password?: string;
|
||||
serial_port?: string;
|
||||
serial_adapter?: Settings["serial"]["adapter"];
|
||||
serial_baudrate?: string;
|
||||
serial_rtscts?: "on";
|
||||
network_channel?: string;
|
||||
network_key?: string;
|
||||
network_pan_id?: string;
|
||||
network_ext_pan_id?: string;
|
||||
frontend_enabled?: "on";
|
||||
frontend_port?: string;
|
||||
homeassistant_enabled?: "on";
|
||||
log_level?: Settings["advanced"]["log_level"];
|
||||
/** same as extension/frontend */
|
||||
const FILE_SERVER_OPTIONS: expressStaticGzip.ExpressStaticGzipOptions = {
|
||||
enableBrotli: true,
|
||||
serveStatic: {
|
||||
/* v8 ignore start */
|
||||
setHeaders: (res: ServerResponse, path: string): void => {
|
||||
if (path.endsWith("index.html")) {
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
}
|
||||
},
|
||||
/* v8 ignore stop */
|
||||
},
|
||||
};
|
||||
|
||||
function escapeHtml(s: string): string {
|
||||
return s.replace(/[^0-9A-Za-z \-_.]/g, (c) => `&#${c.charCodeAt(0)};`);
|
||||
}
|
||||
|
||||
function generateHtmlDone(frontendUrl: string | undefined): string {
|
||||
return `
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Zigbee2MQTT Onboarding</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Zigbee2MQTT Onboarding</h1>
|
||||
<p>Settings saved.</p>
|
||||
<p>Zigbee2MQTT is now starting...</p>
|
||||
<small>${frontendUrl ? `Redirecting to Zigbee2MQTT frontend at <a href="${frontendUrl}">${frontendUrl}</a> in 30 seconds.` : "You can close this page."}</small>
|
||||
</main>
|
||||
${frontendUrl ? `<script>setTimeout(() => { window.location.replace("${frontendUrl}"); }, 30000);</script>` : ""}
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
|
||||
function generateHtmlForm(currentSettings: RecursivePartial<Settings>, devices: Awaited<ReturnType<typeof findAllDevices>>): string {
|
||||
let devicesSelect = "";
|
||||
|
||||
if (devices.length > 0) {
|
||||
devicesSelect += '<select id="found_device" onchange="setFoundDevice(this)">';
|
||||
devicesSelect += '<option value="">Select a device</option>';
|
||||
|
||||
for (const device of devices) {
|
||||
// just in case name has commas, remove them to not mess with `split` logic
|
||||
const deviceStr = `${device.name.replaceAll(",", "")}, ${device.path}, ${device.adapter ?? "unknown"}`;
|
||||
|
||||
devicesSelect += `<option value="${deviceStr}">${deviceStr}</option>`;
|
||||
}
|
||||
|
||||
devicesSelect += "</select>";
|
||||
devicesSelect += "<small>Optionally allows to configure coordinator port and type (if known) automatically.</small>";
|
||||
} else {
|
||||
devicesSelect = "<small>No device found</small>";
|
||||
}
|
||||
|
||||
let generateCheckbox = "";
|
||||
|
||||
if (
|
||||
Array.isArray(currentSettings.advanced?.network_key) ||
|
||||
typeof currentSettings.advanced?.pan_id === "number" ||
|
||||
Array.isArray(currentSettings.advanced?.ext_pan_id)
|
||||
) {
|
||||
generateCheckbox = `
|
||||
<label for="generate_network">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="generate_network"
|
||||
onclick="setGenerate(this)"
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_NETWORK_KEY || process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_PAN_ID || process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_EXT_PAN_ID ? "disabled" : ""}>
|
||||
Generate network?
|
||||
</label>
|
||||
`;
|
||||
}
|
||||
|
||||
/* v8 ignore start */
|
||||
return `
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Zigbee2MQTT Onboarding</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Zigbee2MQTT Onboarding</h1>
|
||||
<p>Set the base configuration to start Zigbee2MQTT.</p>
|
||||
<p>Optional fields will either be ignored or fallback to defaults if not set (see appropriate documentation page for more details).</p>
|
||||
<p>If a field is disabled, it means <a href="https://www.zigbee2mqtt.io/guide/configuration/#environment-variables" target="_blank">environment variables</a> are being used to override specific values (for example, through the Home Assistant add-on configuration page).</p>
|
||||
<hr>
|
||||
<form method="post">
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_SERIAL || process.env.ZIGBEE2MQTT_CONFIG_SERIAL_PORT || process.env.ZIGBEE2MQTT_CONFIG_SERIAL_ADAPTER ? "disabled" : ""}>
|
||||
<label for="found_device">Found Devices</label>
|
||||
${devicesSelect}
|
||||
</fieldset>
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_SERIAL ? "disabled" : ""}>
|
||||
<label for="serial_port">Coordinator/Adapter Port/Path</label>
|
||||
<input
|
||||
type="text"
|
||||
id="serial_port"
|
||||
name="serial_port"
|
||||
value="${currentSettings.serial?.port ?? ""}"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_SERIAL_PORT ? "disabled" : ""}>
|
||||
<label for="serial_adapter">Coordinator/Adapter Type/Stack/Driver</label>
|
||||
<select id="serial_adapter" name="serial_adapter" required ${process.env.ZIGBEE2MQTT_CONFIG_SERIAL_ADAPTER ? "disabled" : ""}>
|
||||
<option value="zstack" ${currentSettings.serial?.adapter === "zstack" ? "selected" : ""}>zstack</option>
|
||||
<option value="ember" ${currentSettings.serial?.adapter === "ember" ? "selected" : ""}>ember</option>
|
||||
<option value="deconz" ${currentSettings.serial?.adapter === "deconz" ? "selected" : ""}>deconz</option>
|
||||
<option value="zigate" ${currentSettings.serial?.adapter === "zigate" ? "selected" : ""}>zigate</option>
|
||||
<option value="zboss" ${currentSettings.serial?.adapter === "zboss" ? "selected" : ""}>zboss</option>
|
||||
</select>
|
||||
<label for="serial_baudrate">Coordinator/Adapter Baudrate</label>
|
||||
<select id="serial_baudrate" name="serial_baudrate" ${process.env.ZIGBEE2MQTT_CONFIG_SERIAL_BAUDRATE ? "disabled" : ""}>
|
||||
<option value="38400" ${currentSettings.serial?.baudrate === 38400 ? "selected" : ""}>38400</option>
|
||||
<option value="57600" ${currentSettings.serial?.baudrate === 57600 ? "selected" : ""}>57600</option>
|
||||
<option value="115200" ${!currentSettings.serial?.baudrate || currentSettings.serial?.baudrate === 115200 ? "selected" : ""}>115200</option>
|
||||
<option value="230400" ${currentSettings.serial?.baudrate === 230400 ? "selected" : ""}>230400</option>
|
||||
<option value="460800" ${currentSettings.serial?.baudrate === 460800 ? "selected" : ""}>460800</option>
|
||||
<option value="921600" ${currentSettings.serial?.baudrate === 921600 ? "selected" : ""}>921600</option>
|
||||
</select>
|
||||
<small>Can be ignored for networked coordinators (TCP).</small>
|
||||
<label for="serial_rtscts">Coordinator/Adapter Hardware Flow Control ("rtscts: true")</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="serial_rtscts"
|
||||
name="serial_rtscts"
|
||||
${currentSettings.serial?.rtscts ? "checked" : ""}
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_SERIAL_RTSCTS ? "disabled" : ""}
|
||||
style="margin-bottom: 1rem;">
|
||||
<small>Can be ignored for networked coordinators (TCP).</small>
|
||||
</fieldset>
|
||||
<small>
|
||||
<a href="https://www.zigbee2mqtt.io/guide/configuration/adapter-settings.html" target="_blank">https://www.zigbee2mqtt.io/guide/configuration/adapter-settings.html</a>
|
||||
</small>
|
||||
<hr>
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED ? "disabled" : ""}>
|
||||
<label for="closest_wifi_channel">Closest WiFi Channel</label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="14"
|
||||
id="closest_wifi_channel"
|
||||
value="0"
|
||||
onclick="setBestZigbeeChannel(this)"
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_CHANNEL ? "disabled" : ""}>
|
||||
<small>Optionally set to your closest WiFi channel to pick the best value for "Network channel" below.</small>
|
||||
<label for="network_channel">Network Channel</label>
|
||||
<input
|
||||
type="number"
|
||||
min="11"
|
||||
max="26"
|
||||
id="network_channel"
|
||||
name="network_channel"
|
||||
value="${currentSettings.advanced?.channel ?? "25"}"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_CHANNEL ? "disabled" : ""}>
|
||||
</fieldset>
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED ? "disabled" : ""}>
|
||||
${generateCheckbox}
|
||||
<label for="network_key">Network Key</label>
|
||||
<input
|
||||
type="text"
|
||||
id="network_key"
|
||||
name="network_key"
|
||||
value="${currentSettings.advanced?.network_key ?? "GENERATE"}"
|
||||
pattern="^([0-9]+(,[0-9]+){15})|GENERATE$"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_NETWORK_KEY ? "disabled" : ""}>
|
||||
<label for="network_pan_id">Network PAN ID</label>
|
||||
<input
|
||||
type="text"
|
||||
id="network_pan_id"
|
||||
name="network_pan_id"
|
||||
value="${currentSettings.advanced?.pan_id ?? "GENERATE"}"
|
||||
pattern="^([0-9]{1,5})|GENERATE$"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_PAN_ID ? "disabled" : ""}>
|
||||
<label for="network_ext_pan_id">Network Extended PAN ID</label>
|
||||
<input
|
||||
type="text"
|
||||
id="network_ext_pan_id"
|
||||
name="network_ext_pan_id"
|
||||
value="${currentSettings.advanced?.ext_pan_id ?? "GENERATE"}"
|
||||
pattern="^([0-9]+(,[0-9]+){7})|GENERATE$"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_EXT_PAN_ID ? "disabled" : ""}>
|
||||
</fieldset>
|
||||
<small>
|
||||
<a href="https://www.zigbee2mqtt.io/guide/configuration/zigbee-network.html" target="_blank">https://www.zigbee2mqtt.io/guide/configuration/zigbee-network.html</a>
|
||||
</small>
|
||||
<hr>
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_MQTT ? "disabled" : ""}>
|
||||
<label for="mqtt_base_topic">MQTT Base Topic</label>
|
||||
<input
|
||||
type="text"
|
||||
id="mqtt_base_topic"
|
||||
name="mqtt_base_topic"
|
||||
value="${currentSettings.mqtt?.base_topic ?? "zigbee2mqtt"}"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_MQTT_BASE_TOPIC ? "disabled" : ""}>
|
||||
<label for="mqtt_server">MQTT Server</label>
|
||||
<input
|
||||
type="text"
|
||||
id="mqtt_server"
|
||||
name="mqtt_server"
|
||||
value="${currentSettings.mqtt?.server ?? "mqtt://localhost:1883"}"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_MQTT_SERVER ? "disabled" : ""}>
|
||||
<label for="mqtt_user">MQTT User</label>
|
||||
<input
|
||||
type="text"
|
||||
id="mqtt_user"
|
||||
name="mqtt_user"
|
||||
value="${currentSettings.mqtt?.user ?? ""}"
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_MQTT_USER ? "disabled" : ""}>
|
||||
<small>Optional. Set only if using authentication.</small>
|
||||
<label for="mqtt_password">MQTT Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="mqtt_password"
|
||||
name="mqtt_password"
|
||||
value="${currentSettings.mqtt?.password ?? ""}"
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_MQTT_PASSWORD ? "disabled" : ""}>
|
||||
<small>Optional. Set only if using authentication.</small>
|
||||
</fieldset>
|
||||
<small>
|
||||
<a href="https://www.zigbee2mqtt.io/guide/configuration/mqtt.html" target="_blank">https://www.zigbee2mqtt.io/guide/configuration/mqtt.html</a>
|
||||
</small>
|
||||
<hr>
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_FRONTEND ? "disabled" : ""}>
|
||||
<label for="frontend_enabled">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="frontend_enabled"
|
||||
name="frontend_enabled"
|
||||
${currentSettings.frontend?.enabled ? "checked" : ""}
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_FRONTEND_ENABLED ? "disabled" : ""}>
|
||||
Frontend enabled?
|
||||
</label>
|
||||
<label for="frontend_port">Frontend Port</label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="65535"
|
||||
id="frontend_port"
|
||||
name="frontend_port"
|
||||
value="${currentSettings.frontend?.port ?? "8080"}"
|
||||
required
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_FRONTEND_PORT ? "disabled" : ""}>
|
||||
</fieldset>
|
||||
<small>
|
||||
<a href="https://www.zigbee2mqtt.io/guide/configuration/frontend.html" target="_blank">https://www.zigbee2mqtt.io/guide/configuration/frontend.html</a>
|
||||
</small>
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_HOMEASSISTANT ? "disabled" : ""}>
|
||||
<label for="homeassistant_enabled">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="homeassistant_enabled"
|
||||
name="homeassistant_enabled"
|
||||
${currentSettings.homeassistant?.enabled ? "checked" : ""}
|
||||
${process.env.ZIGBEE2MQTT_CONFIG_HOMEASSISTANT_ENABLED ? "disabled" : ""}>
|
||||
Home Assistant enabled?
|
||||
</label>
|
||||
</fieldset>
|
||||
<small>
|
||||
<a href="https://www.zigbee2mqtt.io/guide/configuration/homeassistant.html" target="_blank">https://www.zigbee2mqtt.io/guide/configuration/homeassistant.html</a>
|
||||
</small>
|
||||
<hr>
|
||||
<fieldset ${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED ? "disabled" : ""}>
|
||||
<label for="log_level">Log Level</label>
|
||||
<select id="log_level" name="log_level" ${process.env.ZIGBEE2MQTT_CONFIG_ADVANCED_LOG_LEVEL ? "disabled" : ""}>
|
||||
<option value="error" ${currentSettings.advanced?.log_level === "error" ? "selected" : ""}>error</option>
|
||||
<option value="warning" ${currentSettings.advanced?.log_level === "warning" ? "selected" : ""}>warning</option>
|
||||
<option value="info" ${!currentSettings.advanced?.log_level || currentSettings.advanced?.log_level === "info" ? "selected" : ""}>info</option>
|
||||
<option value="debug" ${currentSettings.advanced?.log_level === "debug" ? "selected" : ""}>debug</option>
|
||||
</select>
|
||||
</fieldset>
|
||||
<small>
|
||||
<a href="https://www.zigbee2mqtt.io/guide/configuration/logging.html" target="_blank">https://www.zigbee2mqtt.io/guide/configuration/logging.html</a>
|
||||
</small>
|
||||
<hr>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</main>
|
||||
<script>
|
||||
function setFoundDevice(e) {
|
||||
if (!e.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [, path, adapter] = e.value.split(", ");
|
||||
const serialPortEl = document.querySelector("#serial_port");
|
||||
serialPortEl.value = path;
|
||||
const serialAdapterEl = document.querySelector("#serial_adapter");
|
||||
|
||||
if (['zstack', 'ember', 'deconz', 'zigate', 'zboss'].includes(adapter)) {
|
||||
serialAdapterEl.value = adapter;
|
||||
} else {
|
||||
serialAdapterEl.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function setBestZigbeeChannel(e) {
|
||||
const wifiChannel = parseInt(e.value, 10);
|
||||
const networkChannelEl = document.querySelector("#network_channel");
|
||||
|
||||
if (wifiChannel >= 11) {
|
||||
// WiFi 11-14
|
||||
networkChannelEl.value = 15;
|
||||
} else if (wifiChannel >= 6) {
|
||||
// WiFi 6-10
|
||||
networkChannelEl.value = 11;
|
||||
} else {
|
||||
// WiFi 1-5
|
||||
networkChannelEl.value = 25;
|
||||
}
|
||||
}
|
||||
|
||||
function setGenerate(e) {
|
||||
document.querySelector("#network_key").value = e.checked ? "GENERATE" : "${currentSettings.advanced?.network_key ?? "GENERATE"}";
|
||||
document.querySelector("#network_pan_id").value = e.checked ? "GENERATE" : "${currentSettings.advanced?.pan_id ?? "GENERATE"}";
|
||||
document.querySelector("#network_ext_pan_id").value = e.checked ? "GENERATE" : "${currentSettings.advanced?.ext_pan_id ?? "GENERATE"}";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
/* v8 ignore stop */
|
||||
}
|
||||
|
||||
function generateHtmlError(errors: string): string {
|
||||
return `
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Zigbee2MQTT Onboarding</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Zigbee2MQTT configuration is not valid</h1>
|
||||
<p style="color: #F00;">Found the following errors:</p>
|
||||
${errors}
|
||||
<hr>
|
||||
<p>If you don't know how to solve this, read <a href="https://www.zigbee2mqtt.io/guide/configuration" target="_blank">https://www.zigbee2mqtt.io/guide/configuration</a></p>
|
||||
<form method="post" action="/">
|
||||
<input type="submit" value="Close">
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
|
||||
function getServerUrl(): URL {
|
||||
return new URL(process.env.Z2M_ONBOARD_URL ?? "http://0.0.0.0:8080");
|
||||
}
|
||||
|
||||
function getZipEntryTargetPath(entryName: string): string {
|
||||
const normalizedEntry = entryName.replace(/\\/g, "/");
|
||||
|
||||
if (!normalizedEntry || normalizedEntry.startsWith("/") || normalizedEntry.includes("\0")) {
|
||||
throw new Error(`Invalid ZIP entry path '${entryName}'`);
|
||||
}
|
||||
|
||||
const basePath = path.resolve(data.getPath());
|
||||
const targetPath = path.resolve(basePath, normalizedEntry);
|
||||
const relativePath = path.relative(basePath, targetPath);
|
||||
|
||||
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
||||
throw new Error(`Unsafe ZIP entry path '${entryName}'`);
|
||||
}
|
||||
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
async function extractZipDataToDataPath(zipContent: Buffer): Promise<void> {
|
||||
const zip = await JSZip.loadAsync(zipContent);
|
||||
|
||||
for (const key in zip.files) {
|
||||
const entry = zip.files[key];
|
||||
const targetPath = getZipEntryTargetPath(entry.name);
|
||||
|
||||
if (entry.dir) {
|
||||
mkdirSync(targetPath, {recursive: true});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
mkdirSync(path.dirname(targetPath), {recursive: true});
|
||||
writeFileSync(targetPath, await entry.async("nodebuffer"));
|
||||
}
|
||||
}
|
||||
|
||||
async function startOnboardingServer(): Promise<boolean> {
|
||||
const currentSettings = settings.get();
|
||||
const serverUrl = getServerUrl();
|
||||
let server: ReturnType<typeof createServer> | undefined;
|
||||
let failed = false;
|
||||
const fileServer = expressStaticGzip((await import("zigbee2mqtt-windfront")).default.getOnboardingPath(), FILE_SERVER_OPTIONS);
|
||||
|
||||
const success = await new Promise<boolean>((resolve) => {
|
||||
server = createServer(async (req, res) => {
|
||||
const pathname = new URL(req.url /* v8 ignore next */ ?? "/", serverUrl).pathname;
|
||||
|
||||
if (req.method === "GET" && pathname === "/data") {
|
||||
const payload: OnboardData = {
|
||||
page: "form",
|
||||
settings: currentSettings,
|
||||
settingsSchema: settings.schemaJson,
|
||||
devices: await findAllDevices(),
|
||||
};
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(200);
|
||||
res.end(stringify(payload));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "POST") {
|
||||
if (failed) {
|
||||
res.end(() => {
|
||||
resolve(false);
|
||||
});
|
||||
} else {
|
||||
if (pathname === "/submit") {
|
||||
let body = "";
|
||||
|
||||
req.on("data", (chunk) => {
|
||||
@@ -400,92 +100,108 @@ async function startOnboardingServer(): Promise<boolean> {
|
||||
});
|
||||
|
||||
req.on("end", () => {
|
||||
const result = parse(body) as unknown as OnboardSettings;
|
||||
const frontendEnabled = result.frontend_enabled === "on";
|
||||
const updatedSettings: RecursivePartial<Settings> = {
|
||||
mqtt: {
|
||||
base_topic: result.mqtt_base_topic,
|
||||
server: result.mqtt_server,
|
||||
user: result.mqtt_user || undefined, // empty string => removed
|
||||
password: result.mqtt_password || undefined, // empty string => removed
|
||||
},
|
||||
serial: {
|
||||
port: result.serial_port,
|
||||
adapter: result.serial_adapter,
|
||||
baudrate: result.serial_baudrate ? Number.parseInt(result.serial_baudrate, 10) : undefined,
|
||||
rtscts: result.serial_rtscts === "on",
|
||||
},
|
||||
advanced: {
|
||||
log_level: result.log_level,
|
||||
channel: result.network_channel ? Number.parseInt(result.network_channel, 10) : undefined,
|
||||
network_key: result.network_key
|
||||
? result.network_key === "GENERATE"
|
||||
? result.network_key
|
||||
: result.network_key.split(",").map((v) => Number.parseInt(v, 10))
|
||||
: undefined,
|
||||
pan_id: result.network_pan_id
|
||||
? result.network_pan_id === "GENERATE"
|
||||
? result.network_pan_id
|
||||
: Number.parseInt(result.network_pan_id, 10)
|
||||
: undefined,
|
||||
ext_pan_id: result.network_ext_pan_id
|
||||
? result.network_ext_pan_id === "GENERATE"
|
||||
? result.network_ext_pan_id
|
||||
: result.network_ext_pan_id.split(",").map((v) => Number.parseInt(v, 10))
|
||||
: undefined,
|
||||
},
|
||||
frontend: {
|
||||
enabled: frontendEnabled,
|
||||
port: result.frontend_port ? Number.parseInt(result.frontend_port, 10) : undefined,
|
||||
},
|
||||
homeassistant: {
|
||||
enabled: result.homeassistant_enabled === "on",
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
settings.apply(updatedSettings);
|
||||
const result = (body ? JSON.parse(body) : {}) as RecursivePartial<Zigbee2MQTTSettings>;
|
||||
|
||||
// to redirect, make sure frontend "will be" enabled, and host isn't socket
|
||||
settings.apply(result);
|
||||
|
||||
const appliedSettings = settings.get();
|
||||
const redirect =
|
||||
!process.env.Z2M_ONBOARD_NO_REDIRECT &&
|
||||
frontendEnabled &&
|
||||
(!currentSettings.frontend?.host || !currentSettings.frontend.host.startsWith("/"));
|
||||
const protocol = currentSettings.frontend?.ssl_cert && currentSettings.frontend.ssl_key ? "https" : "http";
|
||||
appliedSettings.frontend.enabled &&
|
||||
!appliedSettings.frontend.host?.startsWith("/");
|
||||
const protocol = appliedSettings.frontend.ssl_cert && appliedSettings.frontend.ssl_key ? "https" : "http";
|
||||
const frontendUrl = redirect
|
||||
? `${protocol}://${appliedSettings.frontend.host ?? "localhost"}:${appliedSettings.frontend.port}${appliedSettings.frontend.base_url}`
|
||||
: null;
|
||||
const payload: OnboardSubmitResponse = {success: true, frontendUrl};
|
||||
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(200);
|
||||
res.end(
|
||||
generateHtmlDone(
|
||||
redirect
|
||||
? /* v8 ignore next */ `${protocol}://${currentSettings.frontend?.host ?? "localhost"}:${currentSettings.frontend?.port ?? "8080"}${currentSettings.frontend?.base_url ?? "/"}`
|
||||
: undefined,
|
||||
),
|
||||
() => {
|
||||
resolve(true);
|
||||
},
|
||||
);
|
||||
res.end(stringify(payload), () => {
|
||||
resolve(true);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to apply configuration: ${(error as Error).message}`);
|
||||
failed = true;
|
||||
|
||||
if (process.env.Z2M_ONBOARD_NO_FAILURE_PAGE) {
|
||||
res.end(() => {
|
||||
resolve(false);
|
||||
});
|
||||
} else {
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.writeHead(406);
|
||||
res.end(generateHtmlError(`<p>${escapeHtml((error as Error).message)}</p>`));
|
||||
}
|
||||
const payload: OnboardSubmitResponse = {success: false, error: (error as Error).message};
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(406);
|
||||
res.end(stringify(payload));
|
||||
}
|
||||
});
|
||||
|
||||
req.on("error", (error: Error) => {
|
||||
console.error(`Failed to parse request body: ${error.message}`);
|
||||
|
||||
const payload: OnboardSubmitResponse = {success: false, error: error.message};
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(406);
|
||||
res.end(stringify(payload));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname === "/submit-zip") {
|
||||
let body = "";
|
||||
|
||||
req.on("data", (chunk) => {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
req.on("end", async () => {
|
||||
try {
|
||||
if (!body) {
|
||||
throw new Error("Invalid ZIP payload: missing content");
|
||||
}
|
||||
|
||||
const zipContent = Buffer.from(body, "base64");
|
||||
|
||||
await extractZipDataToDataPath(zipContent);
|
||||
|
||||
const payload: OnboardSubmitResponse = {success: true, frontendUrl: null};
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(200);
|
||||
res.end(stringify(payload), () => {
|
||||
resolve(true);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to apply ZIP data: ${(error as Error).message}`);
|
||||
|
||||
const payload: OnboardSubmitResponse = {success: false, error: (error as Error).message};
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(406);
|
||||
res.end(stringify(payload));
|
||||
}
|
||||
});
|
||||
|
||||
req.on("error", (error: Error) => {
|
||||
console.error(`Failed to parse ZIP request body: ${error.message}`);
|
||||
|
||||
const payload: OnboardSubmitResponse = {success: false, error: error.message};
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(406);
|
||||
res.end(stringify(payload));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.writeHead(200);
|
||||
res.end(generateHtmlForm(currentSettings, await findAllDevices()));
|
||||
}
|
||||
|
||||
const next = finalhandler(req, res);
|
||||
|
||||
fileServer(req, res, next);
|
||||
});
|
||||
|
||||
server.on("error", (error: Error) => {
|
||||
console.error("Failed to start onboarding server", error);
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
server.listen(Number.parseInt(serverUrl.port, 10), serverUrl.hostname, () => {
|
||||
@@ -498,21 +214,37 @@ async function startOnboardingServer(): Promise<boolean> {
|
||||
return success;
|
||||
}
|
||||
|
||||
async function startFailureServer(errors: string): Promise<void> {
|
||||
async function startFailureServer(errors: string[]): Promise<void> {
|
||||
const serverUrl = getServerUrl();
|
||||
let server: ReturnType<typeof createServer> | undefined;
|
||||
const fileServer = expressStaticGzip((await import("zigbee2mqtt-windfront")).default.getOnboardingPath(), FILE_SERVER_OPTIONS);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
server = createServer((req, res) => {
|
||||
if (req.method === "POST") {
|
||||
const pathname = new URL(req.url /* v8 ignore next */ ?? "/", serverUrl).pathname;
|
||||
|
||||
if (req.method === "GET" && pathname === "/data") {
|
||||
const payload: OnboardFailureData = {page: "failure", errors};
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.writeHead(200);
|
||||
res.end(stringify(payload));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/submit") {
|
||||
res.writeHead(200);
|
||||
res.end(() => {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.writeHead(406);
|
||||
res.end(generateHtmlError(errors));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const next = finalhandler(req, res);
|
||||
|
||||
fileServer(req, res, next);
|
||||
});
|
||||
|
||||
server.listen(Number.parseInt(serverUrl.port, 10), serverUrl.hostname, () => {
|
||||
@@ -524,23 +256,19 @@ async function startFailureServer(errors: string): Promise<void> {
|
||||
}
|
||||
|
||||
async function onSettingsErrors(errors: string[]): Promise<void> {
|
||||
let pErrors = "";
|
||||
|
||||
console.error("\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
console.error(" READ THIS CAREFULLY\n");
|
||||
console.error("Refusing to start because configuration is not valid, found the following errors:");
|
||||
|
||||
for (const error of errors) {
|
||||
console.error(`- ${error}`);
|
||||
|
||||
pErrors += `<p>- ${escapeHtml(error)}</p>`;
|
||||
}
|
||||
|
||||
console.error("\nIf you don't know how to solve this, read https://www.zigbee2mqtt.io/guide/configuration");
|
||||
console.error("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
|
||||
|
||||
if (!process.env.Z2M_ONBOARD_NO_SERVER && !process.env.Z2M_ONBOARD_NO_FAILURE_PAGE) {
|
||||
await startFailureServer(pErrors);
|
||||
await startFailureServer(errors);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zigbee2mqtt",
|
||||
"version": "2.9.1",
|
||||
"version": "2.9.2",
|
||||
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
|
||||
"main": "index.js",
|
||||
"types": "dist/types/api.d.ts",
|
||||
@@ -52,7 +52,7 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"jszip": "^3.10.1",
|
||||
"mqtt": "^5.15.0",
|
||||
"mqtt": "^5.15.1",
|
||||
"object-assign-deep": "^0.4.0",
|
||||
"rimraf": "^6.1.3",
|
||||
"semver": "^7.7.4",
|
||||
@@ -61,18 +61,18 @@
|
||||
"winston": "^3.19.0",
|
||||
"winston-syslog": "^2.7.1",
|
||||
"winston-transport": "^4.9.0",
|
||||
"ws": "^8.19.0",
|
||||
"zigbee-herdsman": "9.0.9",
|
||||
"zigbee-herdsman-converters": "26.12.0",
|
||||
"ws": "^8.20.0",
|
||||
"zigbee-herdsman": "10.0.5",
|
||||
"zigbee-herdsman-converters": "26.27.0",
|
||||
"zigbee2mqtt-frontend": "0.9.21",
|
||||
"zigbee2mqtt-windfront": "2.9.2"
|
||||
"zigbee2mqtt-windfront": "2.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.4.4",
|
||||
"@biomejs/biome": "^2.4.10",
|
||||
"@types/finalhandler": "^1.2.3",
|
||||
"@types/humanize-duration": "^3.27.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^24.11.0",
|
||||
"@types/node": "^24.12.0",
|
||||
"@types/object-assign-deep": "^0.4.3",
|
||||
"@types/readable-stream": "4.0.23",
|
||||
"@types/serve-static": "^2.2.0",
|
||||
@@ -99,4 +99,4 @@
|
||||
"optionalDependencies": {
|
||||
"unix-dgram": "^2.0.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+131
-131
@@ -5,7 +5,7 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
zigbee-herdsman: 9.0.9
|
||||
zigbee-herdsman: 10.0.5
|
||||
|
||||
importers:
|
||||
|
||||
@@ -42,8 +42,8 @@ importers:
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1
|
||||
mqtt:
|
||||
specifier: ^5.15.0
|
||||
version: 5.15.0
|
||||
specifier: ^5.15.1
|
||||
version: 5.15.1
|
||||
object-assign-deep:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0
|
||||
@@ -69,24 +69,24 @@ importers:
|
||||
specifier: ^4.9.0
|
||||
version: 4.9.0
|
||||
ws:
|
||||
specifier: ^8.19.0
|
||||
version: 8.19.0
|
||||
specifier: ^8.20.0
|
||||
version: 8.20.0
|
||||
zigbee-herdsman:
|
||||
specifier: 9.0.9
|
||||
version: 9.0.9
|
||||
specifier: 10.0.5
|
||||
version: 10.0.5
|
||||
zigbee-herdsman-converters:
|
||||
specifier: 26.12.0
|
||||
version: 26.12.0
|
||||
specifier: 26.27.0
|
||||
version: 26.27.0
|
||||
zigbee2mqtt-frontend:
|
||||
specifier: 0.9.21
|
||||
version: 0.9.21
|
||||
zigbee2mqtt-windfront:
|
||||
specifier: 2.9.2
|
||||
version: 2.9.2
|
||||
specifier: 2.11.1
|
||||
version: 2.11.1
|
||||
devDependencies:
|
||||
'@biomejs/biome':
|
||||
specifier: ^2.4.4
|
||||
version: 2.4.4
|
||||
specifier: ^2.4.10
|
||||
version: 2.4.10
|
||||
'@types/finalhandler':
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.4
|
||||
@@ -97,8 +97,8 @@ importers:
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.9
|
||||
'@types/node':
|
||||
specifier: ^24.11.0
|
||||
version: 24.11.0
|
||||
specifier: ^24.12.0
|
||||
version: 24.12.0
|
||||
'@types/object-assign-deep':
|
||||
specifier: ^0.4.3
|
||||
version: 0.4.3
|
||||
@@ -113,7 +113,7 @@ importers:
|
||||
version: 8.18.1
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.1.1
|
||||
version: 3.2.4(vitest@3.2.4(@types/node@24.11.0))
|
||||
version: 3.2.4(vitest@3.2.4(@types/node@24.12.0))
|
||||
tmp:
|
||||
specifier: ^0.2.5
|
||||
version: 0.2.5
|
||||
@@ -122,7 +122,7 @@ importers:
|
||||
version: 5.9.3
|
||||
vitest:
|
||||
specifier: ^3.1.1
|
||||
version: 3.2.4(@types/node@24.11.0)
|
||||
version: 3.2.4(@types/node@24.12.0)
|
||||
optionalDependencies:
|
||||
unix-dgram:
|
||||
specifier: ^2.0.7
|
||||
@@ -147,8 +147,8 @@ packages:
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/runtime@7.28.6':
|
||||
resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
|
||||
'@babel/runtime@7.29.2':
|
||||
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/types@7.27.6':
|
||||
@@ -159,55 +159,55 @@ packages:
|
||||
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@biomejs/biome@2.4.4':
|
||||
resolution: {integrity: sha512-tigwWS5KfJf0cABVd52NVaXyAVv4qpUXOWJ1rxFL8xF1RVoeS2q/LK+FHgYoKMclJCuRoCWAPy1IXaN9/mS61Q==}
|
||||
'@biomejs/biome@2.4.10':
|
||||
resolution: {integrity: sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
hasBin: true
|
||||
|
||||
'@biomejs/cli-darwin-arm64@2.4.4':
|
||||
resolution: {integrity: sha512-jZ+Xc6qvD6tTH5jM6eKX44dcbyNqJHssfl2nnwT6vma6B1sj7ZLTGIk6N5QwVBs5xGN52r3trk5fgd3sQ9We9A==}
|
||||
'@biomejs/cli-darwin-arm64@2.4.10':
|
||||
resolution: {integrity: sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@biomejs/cli-darwin-x64@2.4.4':
|
||||
resolution: {integrity: sha512-Dh1a/+W+SUCXhEdL7TiX3ArPTFCQKJTI1mGncZNWfO+6suk+gYA4lNyJcBB+pwvF49uw0pEbUS49BgYOY4hzUg==}
|
||||
'@biomejs/cli-darwin-x64@2.4.10':
|
||||
resolution: {integrity: sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@biomejs/cli-linux-arm64-musl@2.4.4':
|
||||
resolution: {integrity: sha512-+sPAXq3bxmFwhVFJnSwkSF5Rw2ZAJMH3MF6C9IveAEOdSpgajPhoQhbbAK12SehN9j2QrHpk4J/cHsa/HqWaYQ==}
|
||||
'@biomejs/cli-linux-arm64-musl@2.4.10':
|
||||
resolution: {integrity: sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-arm64@2.4.4':
|
||||
resolution: {integrity: sha512-V/NFfbWhsUU6w+m5WYbBenlEAz8eYnSqRMDMAW3K+3v0tYVkNyZn8VU0XPxk/lOqNXLSCCrV7FmV/u3SjCBShg==}
|
||||
'@biomejs/cli-linux-arm64@2.4.10':
|
||||
resolution: {integrity: sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-x64-musl@2.4.4':
|
||||
resolution: {integrity: sha512-gGvFTGpOIQDb5CQ2VC0n9Z2UEqlP46c4aNgHmAMytYieTGEcfqhfCFnhs6xjt0S3igE6q5GLuIXtdQt3Izok+g==}
|
||||
'@biomejs/cli-linux-x64-musl@2.4.10':
|
||||
resolution: {integrity: sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-x64@2.4.4':
|
||||
resolution: {integrity: sha512-R4+ZCDtG9kHArasyBO+UBD6jr/FcFCTH8QkNTOCu0pRJzCWyWC4EtZa2AmUZB5h3e0jD7bRV2KvrENcf8rndBg==}
|
||||
'@biomejs/cli-linux-x64@2.4.10':
|
||||
resolution: {integrity: sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-win32-arm64@2.4.4':
|
||||
resolution: {integrity: sha512-trzCqM7x+Gn832zZHgr28JoYagQNX4CZkUZhMUac2YxvvyDRLJDrb5m9IA7CaZLlX6lTQmADVfLEKP1et1Ma4Q==}
|
||||
'@biomejs/cli-win32-arm64@2.4.10':
|
||||
resolution: {integrity: sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@biomejs/cli-win32-x64@2.4.4':
|
||||
resolution: {integrity: sha512-gnOHKVPFAAPrpoPt2t+Q6FZ7RPry/FDV3GcpU53P3PtLNnQjBmKyN2Vh/JtqXet+H4pme8CC76rScwdjDcT1/A==}
|
||||
'@biomejs/cli-win32-x64@2.4.10':
|
||||
resolution: {integrity: sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -553,8 +553,8 @@ packages:
|
||||
'@types/js-yaml@4.0.9':
|
||||
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
|
||||
|
||||
'@types/node@24.11.0':
|
||||
resolution: {integrity: sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==}
|
||||
'@types/node@24.12.0':
|
||||
resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==}
|
||||
|
||||
'@types/object-assign-deep@0.4.3':
|
||||
resolution: {integrity: sha512-d9Gxaj5j1hzrxJ61EFEg13B4g4FgrT/DYtcDWFXPehR8DF2SUZbVMFtZIs8exkVRiqrqBpdTc/lUUZjncsPpMw==}
|
||||
@@ -674,8 +674,8 @@ packages:
|
||||
resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
broker-factory@3.1.13:
|
||||
resolution: {integrity: sha512-H2VALe31mEtO/SRcNp4cUU5BAm1biwhc/JaF77AigUuni/1YT0FLCJfbUxwIEs9y6Kssjk2fmXgf+Y9ALvmKlw==}
|
||||
broker-factory@3.1.14:
|
||||
resolution: {integrity: sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==}
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
@@ -830,8 +830,8 @@ packages:
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
fast-unique-numbers@9.0.26:
|
||||
resolution: {integrity: sha512-3Mtq8p1zQinjGyWfKeuBunbuFoixG72AUkk4VvzbX4ykCW9Q4FzRaNyIlfQhUjnKw2ARVP+/CKnoyr6wfHftig==}
|
||||
fast-unique-numbers@9.0.27:
|
||||
resolution: {integrity: sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==}
|
||||
engines: {node: '>=18.2.0'}
|
||||
|
||||
fast-uri@3.1.0:
|
||||
@@ -990,8 +990,8 @@ packages:
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
lru-cache@11.2.6:
|
||||
resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==}
|
||||
lru-cache@11.2.7:
|
||||
resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
magic-string@0.30.17:
|
||||
@@ -1030,8 +1030,8 @@ packages:
|
||||
mqtt-packet@9.0.2:
|
||||
resolution: {integrity: sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==}
|
||||
|
||||
mqtt@5.15.0:
|
||||
resolution: {integrity: sha512-KC+wAssYk83Qu5bT8YDzDYgUJxPhbLeVsDvpY2QvL28PnXYJzC2WkKruyMUgBAZaQ7h9lo9k2g4neRNUUxzgMw==}
|
||||
mqtt@5.15.1:
|
||||
resolution: {integrity: sha512-V1WnkGuJh3ec9QXzy5Iylw8OOBK+Xu1WhxcQ9mMpLThG+/JZIMV1PgLNRgIiqXhZnvnVLsuyxHl5A/3bHHbcAA==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1440,17 +1440,17 @@ packages:
|
||||
resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
worker-factory@7.0.48:
|
||||
resolution: {integrity: sha512-CGmBy3tJvpBPjUvb0t4PrpKubUsfkI1Ohg0/GGFU2RvA9j/tiVYwKU8O7yu7gH06YtzbeJLzdUR29lmZKn5pag==}
|
||||
worker-factory@7.0.49:
|
||||
resolution: {integrity: sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==}
|
||||
|
||||
worker-timers-broker@8.0.15:
|
||||
resolution: {integrity: sha512-Te+EiVUMzG5TtHdmaBZvBrZSFNauym6ImDaCAnzQUxvjnw+oGjMT2idmAOgDy30vOZMLejd0bcsc90Axu6XPWA==}
|
||||
worker-timers-broker@8.0.16:
|
||||
resolution: {integrity: sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==}
|
||||
|
||||
worker-timers-worker@9.0.13:
|
||||
resolution: {integrity: sha512-qjn18szGb1kjcmh2traAdki1eiIS5ikFo+L90nfMOvSRpuDw1hAcR1nzkP2+Hkdqz5thIRnfuWx7QSpsEUsA6Q==}
|
||||
worker-timers-worker@9.0.14:
|
||||
resolution: {integrity: sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==}
|
||||
|
||||
worker-timers@8.0.29:
|
||||
resolution: {integrity: sha512-9jk0MWHhWAZ2xlJPXr45oe5UF/opdpfZrY0HtyPizWuJ+ce1M3IYk/4IIdGct3kn9Ncfs+tkZt3w1tU6KW2Fsg==}
|
||||
worker-timers@8.0.31:
|
||||
resolution: {integrity: sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
@@ -1460,8 +1460,8 @@ packages:
|
||||
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ws@8.19.0:
|
||||
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
|
||||
ws@8.20.0:
|
||||
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
@@ -1472,12 +1472,12 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
zigbee-herdsman-converters@26.12.0:
|
||||
resolution: {integrity: sha512-Cvla9M0+05FPuFyVb9Qzq6WWZqM7fq5Znu1OmQAdBZBfeIwnc750JKM9fTKjC4kquPIsbrekQ9sNwoJEet4YxQ==}
|
||||
zigbee-herdsman-converters@26.27.0:
|
||||
resolution: {integrity: sha512-l0Tc/TBRML5lO298NRxr2CtKcQ5YxA96FINJuifyGGG62mANSa+UmAcvl0P/fnPo8r7q8wdCYxXrb/7lYILZKA==}
|
||||
engines: {node: '>=20.15.0'}
|
||||
|
||||
zigbee-herdsman@9.0.9:
|
||||
resolution: {integrity: sha512-Q15/Kl028AznkcWlxeD9a7OIHK06rO6+981CuPRXxM+wlHl5dFIu/HZlUwcFkpDtFBBcCvJuWzqwkuXVHAG9Rw==}
|
||||
zigbee-herdsman@10.0.5:
|
||||
resolution: {integrity: sha512-zCXpvzTxQ9pkuYpMV6PVb6gX8AUGpl99ut80Q96bWCqXKHNnkbn1wwrpdaXuvGhoWVB8En9hp8ZnipbF9sODEw==}
|
||||
|
||||
zigbee-on-host@0.2.4:
|
||||
resolution: {integrity: sha512-NIG6CWp+Yfn7PjqEIRvenHqpwT1U7rSkyimnFOUIFpnmxQOJKrmcwWDfn6WjbU/YIYYWSQPlj+g13icu1xwlyg==}
|
||||
@@ -1487,8 +1487,8 @@ packages:
|
||||
resolution: {integrity: sha512-ClHYlG7g0v/tnIsD6kycNCi1ZEKIbQuU7lGJR3WqYcDmuqEMVcHDGbRiRwYRQcZGMxkqCf7guxNsXiKnC10+kg==}
|
||||
engines: {node: '>=20.11'}
|
||||
|
||||
zigbee2mqtt-windfront@2.9.2:
|
||||
resolution: {integrity: sha512-weP7q0hLqXRf4tc94tzl2NGxl3DXMopgCwluMD3HKQiRMyjt3F1xy3NeoJu6Q2o1644cbRg8C+SQnVQUZJVyCw==}
|
||||
zigbee2mqtt-windfront@2.11.1:
|
||||
resolution: {integrity: sha512-2xM01JnuCWZlb1xYkIBVFAGJ6mkuAq/DRi8kfmB8KlA77LzHuX+f85pdgDotF9WGWb4+TqxmnpifYri1Nc3K2A==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
|
||||
snapshots:
|
||||
@@ -1506,7 +1506,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 7.27.6
|
||||
|
||||
'@babel/runtime@7.28.6': {}
|
||||
'@babel/runtime@7.29.2': {}
|
||||
|
||||
'@babel/types@7.27.6':
|
||||
dependencies:
|
||||
@@ -1515,39 +1515,39 @@ snapshots:
|
||||
|
||||
'@bcoe/v8-coverage@1.0.2': {}
|
||||
|
||||
'@biomejs/biome@2.4.4':
|
||||
'@biomejs/biome@2.4.10':
|
||||
optionalDependencies:
|
||||
'@biomejs/cli-darwin-arm64': 2.4.4
|
||||
'@biomejs/cli-darwin-x64': 2.4.4
|
||||
'@biomejs/cli-linux-arm64': 2.4.4
|
||||
'@biomejs/cli-linux-arm64-musl': 2.4.4
|
||||
'@biomejs/cli-linux-x64': 2.4.4
|
||||
'@biomejs/cli-linux-x64-musl': 2.4.4
|
||||
'@biomejs/cli-win32-arm64': 2.4.4
|
||||
'@biomejs/cli-win32-x64': 2.4.4
|
||||
'@biomejs/cli-darwin-arm64': 2.4.10
|
||||
'@biomejs/cli-darwin-x64': 2.4.10
|
||||
'@biomejs/cli-linux-arm64': 2.4.10
|
||||
'@biomejs/cli-linux-arm64-musl': 2.4.10
|
||||
'@biomejs/cli-linux-x64': 2.4.10
|
||||
'@biomejs/cli-linux-x64-musl': 2.4.10
|
||||
'@biomejs/cli-win32-arm64': 2.4.10
|
||||
'@biomejs/cli-win32-x64': 2.4.10
|
||||
|
||||
'@biomejs/cli-darwin-arm64@2.4.4':
|
||||
'@biomejs/cli-darwin-arm64@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-darwin-x64@2.4.4':
|
||||
'@biomejs/cli-darwin-x64@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-arm64-musl@2.4.4':
|
||||
'@biomejs/cli-linux-arm64-musl@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-arm64@2.4.4':
|
||||
'@biomejs/cli-linux-arm64@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-x64-musl@2.4.4':
|
||||
'@biomejs/cli-linux-x64-musl@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-x64@2.4.4':
|
||||
'@biomejs/cli-linux-x64@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-win32-arm64@2.4.4':
|
||||
'@biomejs/cli-win32-arm64@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-win32-x64@2.4.4':
|
||||
'@biomejs/cli-win32-x64@2.4.10':
|
||||
optional: true
|
||||
|
||||
'@colors/colors@1.6.0': {}
|
||||
@@ -1770,7 +1770,7 @@ snapshots:
|
||||
|
||||
'@types/finalhandler@1.2.4':
|
||||
dependencies:
|
||||
'@types/node': 24.11.0
|
||||
'@types/node': 24.12.0
|
||||
|
||||
'@types/http-errors@2.0.5': {}
|
||||
|
||||
@@ -1778,7 +1778,7 @@ snapshots:
|
||||
|
||||
'@types/js-yaml@4.0.9': {}
|
||||
|
||||
'@types/node@24.11.0':
|
||||
'@types/node@24.12.0':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
|
||||
@@ -1786,20 +1786,20 @@ snapshots:
|
||||
|
||||
'@types/readable-stream@4.0.23':
|
||||
dependencies:
|
||||
'@types/node': 24.11.0
|
||||
'@types/node': 24.12.0
|
||||
|
||||
'@types/serve-static@2.2.0':
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.5
|
||||
'@types/node': 24.11.0
|
||||
'@types/node': 24.12.0
|
||||
|
||||
'@types/triple-beam@1.3.5': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 24.11.0
|
||||
'@types/node': 24.12.0
|
||||
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.11.0))':
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.12.0))':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@bcoe/v8-coverage': 1.0.2
|
||||
@@ -1814,7 +1814,7 @@ snapshots:
|
||||
std-env: 3.9.0
|
||||
test-exclude: 7.0.1
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/node@24.11.0)
|
||||
vitest: 3.2.4(@types/node@24.12.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -1826,13 +1826,13 @@ snapshots:
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.11.0))':
|
||||
'@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.12.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
optionalDependencies:
|
||||
vite: 6.3.5(@types/node@24.11.0)
|
||||
vite: 6.3.5(@types/node@24.12.0)
|
||||
|
||||
'@vitest/pretty-format@3.2.4':
|
||||
dependencies:
|
||||
@@ -1928,12 +1928,12 @@ snapshots:
|
||||
dependencies:
|
||||
balanced-match: 4.0.2
|
||||
|
||||
broker-factory@3.1.13:
|
||||
broker-factory@3.1.14:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
fast-unique-numbers: 9.0.26
|
||||
'@babel/runtime': 7.29.2
|
||||
fast-unique-numbers: 9.0.27
|
||||
tslib: 2.8.1
|
||||
worker-factory: 7.0.48
|
||||
worker-factory: 7.0.49
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
@@ -2080,9 +2080,9 @@ snapshots:
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-unique-numbers@9.0.26:
|
||||
fast-unique-numbers@9.0.27:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
'@babel/runtime': 7.29.2
|
||||
tslib: 2.8.1
|
||||
|
||||
fast-uri@3.1.0: {}
|
||||
@@ -2183,7 +2183,7 @@ snapshots:
|
||||
istanbul-lib-source-maps@5.0.6:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
debug: 4.4.3
|
||||
debug: 4.4.1
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -2241,7 +2241,7 @@ snapshots:
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@11.2.6: {}
|
||||
lru-cache@11.2.7: {}
|
||||
|
||||
magic-string@0.30.17:
|
||||
dependencies:
|
||||
@@ -2283,7 +2283,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
mqtt@5.15.0:
|
||||
mqtt@5.15.1:
|
||||
dependencies:
|
||||
'@types/readable-stream': 4.0.23
|
||||
'@types/ws': 8.18.1
|
||||
@@ -2299,8 +2299,8 @@ snapshots:
|
||||
rfdc: 1.4.1
|
||||
socks: 2.8.7
|
||||
split2: 4.2.0
|
||||
worker-timers: 8.0.29
|
||||
ws: 8.19.0
|
||||
worker-timers: 8.0.31
|
||||
ws: 8.20.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
@@ -2354,7 +2354,7 @@ snapshots:
|
||||
|
||||
path-scurry@2.0.1:
|
||||
dependencies:
|
||||
lru-cache: 11.2.6
|
||||
lru-cache: 11.2.7
|
||||
minipass: 7.1.2
|
||||
|
||||
pathe@2.0.3: {}
|
||||
@@ -2606,13 +2606,13 @@ snapshots:
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
vite-node@3.2.4(@types/node@24.11.0):
|
||||
vite-node@3.2.4(@types/node@24.12.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.3
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 6.3.5(@types/node@24.11.0)
|
||||
vite: 6.3.5(@types/node@24.12.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
@@ -2627,7 +2627,7 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite@6.3.5(@types/node@24.11.0):
|
||||
vite@6.3.5(@types/node@24.12.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.5
|
||||
fdir: 6.4.6(picomatch@4.0.2)
|
||||
@@ -2636,14 +2636,14 @@ snapshots:
|
||||
rollup: 4.44.0
|
||||
tinyglobby: 0.2.14
|
||||
optionalDependencies:
|
||||
'@types/node': 24.11.0
|
||||
'@types/node': 24.12.0
|
||||
fsevents: 2.3.3
|
||||
|
||||
vitest@3.2.4(@types/node@24.11.0):
|
||||
vitest@3.2.4(@types/node@24.12.0):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/expect': 3.2.4
|
||||
'@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.11.0))
|
||||
'@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.12.0))
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
'@vitest/runner': 3.2.4
|
||||
'@vitest/snapshot': 3.2.4
|
||||
@@ -2661,11 +2661,11 @@ snapshots:
|
||||
tinyglobby: 0.2.14
|
||||
tinypool: 1.1.1
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 6.3.5(@types/node@24.11.0)
|
||||
vite-node: 3.2.4(@types/node@24.11.0)
|
||||
vite: 6.3.5(@types/node@24.12.0)
|
||||
vite-node: 3.2.4(@types/node@24.12.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 24.11.0
|
||||
'@types/node': 24.12.0
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- less
|
||||
@@ -2718,32 +2718,32 @@ snapshots:
|
||||
triple-beam: 1.4.1
|
||||
winston-transport: 4.9.0
|
||||
|
||||
worker-factory@7.0.48:
|
||||
worker-factory@7.0.49:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
fast-unique-numbers: 9.0.26
|
||||
'@babel/runtime': 7.29.2
|
||||
fast-unique-numbers: 9.0.27
|
||||
tslib: 2.8.1
|
||||
|
||||
worker-timers-broker@8.0.15:
|
||||
worker-timers-broker@8.0.16:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
broker-factory: 3.1.13
|
||||
fast-unique-numbers: 9.0.26
|
||||
'@babel/runtime': 7.29.2
|
||||
broker-factory: 3.1.14
|
||||
fast-unique-numbers: 9.0.27
|
||||
tslib: 2.8.1
|
||||
worker-timers-worker: 9.0.13
|
||||
worker-timers-worker: 9.0.14
|
||||
|
||||
worker-timers-worker@9.0.13:
|
||||
worker-timers-worker@9.0.14:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
'@babel/runtime': 7.29.2
|
||||
tslib: 2.8.1
|
||||
worker-factory: 7.0.48
|
||||
worker-factory: 7.0.49
|
||||
|
||||
worker-timers@8.0.29:
|
||||
worker-timers@8.0.31:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
'@babel/runtime': 7.29.2
|
||||
tslib: 2.8.1
|
||||
worker-timers-broker: 8.0.15
|
||||
worker-timers-worker: 9.0.13
|
||||
worker-timers-broker: 8.0.16
|
||||
worker-timers-worker: 9.0.14
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
@@ -2757,17 +2757,17 @@ snapshots:
|
||||
string-width: 5.1.2
|
||||
strip-ansi: 7.1.2
|
||||
|
||||
ws@8.19.0: {}
|
||||
ws@8.20.0: {}
|
||||
|
||||
zigbee-herdsman-converters@26.12.0:
|
||||
zigbee-herdsman-converters@26.27.0:
|
||||
dependencies:
|
||||
iconv-lite: 0.7.2
|
||||
semver: 7.7.4
|
||||
zigbee-herdsman: 9.0.9
|
||||
zigbee-herdsman: 10.0.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
zigbee-herdsman@9.0.9:
|
||||
zigbee-herdsman@10.0.5:
|
||||
dependencies:
|
||||
'@date-fns/tz': 1.4.1
|
||||
'@serialport/bindings-cpp': 13.0.1
|
||||
@@ -2785,4 +2785,4 @@ snapshots:
|
||||
|
||||
zigbee2mqtt-frontend@0.9.21: {}
|
||||
|
||||
zigbee2mqtt-windfront@2.9.2: {}
|
||||
zigbee2mqtt-windfront@2.11.1: {}
|
||||
|
||||
@@ -18,5 +18,5 @@ const sandbox = {
|
||||
};
|
||||
vm.runInNewContext(moduleCode, sandbox, moduleFakePath);
|
||||
const converter = sandbox.module.exports;
|
||||
assert(!converter.toZigbee || !converter.toZigbee.includes(undefined));
|
||||
assert(!converter.fromZigbee || !converter.fromZigbee.includes(undefined));
|
||||
assert(!converter.toZigbee?.includes(undefined));
|
||||
assert(!converter.fromZigbee?.includes(undefined));
|
||||
|
||||
@@ -112,7 +112,7 @@ describe("Controller", () => {
|
||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||
"bulb (0x000b57fffec6a5b2): LED1545G12 - IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (Router)",
|
||||
);
|
||||
expect(mockLogger.info).toHaveBeenCalledWith("remote (0x0017880104e45517): 324131092621 - Philips Hue dimmer switch (EndDevice)");
|
||||
expect(mockLogger.info).toHaveBeenCalledWith("remote (0x0017880104e45517): 324131092621 - Philips Hue dimmer switch gen 1 (EndDevice)");
|
||||
expect(mockLogger.info).toHaveBeenCalledWith("0x0017880104e45518 (0x0017880104e45518): Not supported (EndDevice)");
|
||||
expect(mockMQTTConnectAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTConnectAsync).toHaveBeenCalledWith("mqtt://localhost", {
|
||||
|
||||
@@ -597,6 +597,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: true,
|
||||
vendor: "IKEA",
|
||||
version: "0.0.0",
|
||||
},
|
||||
description: "this is my bulb",
|
||||
disabled: false,
|
||||
@@ -802,6 +803,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: true,
|
||||
vendor: "Philips",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {
|
||||
@@ -831,7 +833,7 @@ describe("Extension: Bridge", () => {
|
||||
{
|
||||
definition: {
|
||||
source: "native",
|
||||
description: "Hue dimmer switch",
|
||||
description: "Hue dimmer switch gen 1",
|
||||
exposes: [
|
||||
{
|
||||
access: 1,
|
||||
@@ -930,6 +932,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: true,
|
||||
vendor: "Philips",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {
|
||||
@@ -1038,6 +1041,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: false,
|
||||
vendor: "notSupportedMfg",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {
|
||||
@@ -1143,6 +1147,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: false,
|
||||
vendor: "Aqara",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {
|
||||
@@ -1304,6 +1309,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: false,
|
||||
vendor: "Aqara",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {"1": {bindings: [], clusters: {input: ["genBasic"], output: []}, configured_reportings: [], scenes: []}},
|
||||
@@ -1455,6 +1461,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: true,
|
||||
vendor: "Xiaomi",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {"1": {bindings: [], clusters: {input: ["genBasic", "genOnOff"], output: []}, configured_reportings: [], scenes: []}},
|
||||
@@ -1914,6 +1921,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: false,
|
||||
vendor: "Siglis",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {
|
||||
@@ -2204,6 +2212,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: true,
|
||||
vendor: "IKEA",
|
||||
version: "0.0.0",
|
||||
},
|
||||
disabled: false,
|
||||
endpoints: {
|
||||
@@ -2639,6 +2648,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: true,
|
||||
vendor: "IKEA",
|
||||
version: "0.0.0",
|
||||
},
|
||||
friendly_name: "bulb",
|
||||
ieee_address: "0x000b57fffec6a5b2",
|
||||
@@ -2725,6 +2735,7 @@ describe("Extension: Bridge", () => {
|
||||
],
|
||||
supports_ota: false,
|
||||
vendor: "notSupportedMfg",
|
||||
version: "0.0.0",
|
||||
},
|
||||
friendly_name: "0x0017880104e45518",
|
||||
ieee_address: "0x0017880104e45518",
|
||||
|
||||
+710
-146
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user