diff --git a/packages/api/routes/groups.ts b/packages/api/routes/groups.ts index 8bbcd04..a617ff9 100644 --- a/packages/api/routes/groups.ts +++ b/packages/api/routes/groups.ts @@ -4,7 +4,7 @@ import express, { Request, Response, Router } from "express"; import { Zigbee2MqttService } from "../zigbee2mqttService"; import { ApiError } from "./api"; -import { range } from "../utils"; +import { nextUnused, range } from "../utils"; import { type Group, Scene } from "@starlight/types"; const router = express.Router(); @@ -277,14 +277,10 @@ export function groupsRouter(zigbee2mqttService: Zigbee2MqttService): Router { return res.status(404).json({ error: ApiError.NameInUse }); } - let sceneId: number = 0; - const idRange = range(0, 255); - - if (group.group.scenes.length !== 0) { - const usedIds = new Set(group.group.scenes.map((scene) => scene.id)); - const feasibleIds = idRange.filter((value) => !usedIds.has(value)); - sceneId = Math.min(...feasibleIds); - } + let sceneId = nextUnused( + group.group.scenes.map((scene) => scene.id), + range(0, 255), + ); // transition on scenes can only be set with 'scene_add', so if we have the // property in the body we should first call createOrUpdateScene with just diff --git a/packages/api/utils.ts b/packages/api/utils.ts index c29780a..e3eebaa 100644 --- a/packages/api/utils.ts +++ b/packages/api/utils.ts @@ -51,3 +51,15 @@ export function range( .fill(start) .map((_, index) => start + index * step); } + +export function nextUnused(items: Array, range: Array): number { + let nextUnused: number = 0; + + if (items.length !== 0) { + const usedNumbers = new Set(items); + const feasibleNumbers = range.filter((value) => !usedNumbers.has(value)); + nextUnused = Math.min(...feasibleNumbers); + } + + return nextUnused; +} diff --git a/packages/ui/src/components/GroupSettings/GroupSettings.tsx b/packages/ui/src/components/GroupSettings/GroupSettings.tsx index 890da3f..8851fe8 100644 --- a/packages/ui/src/components/GroupSettings/GroupSettings.tsx +++ b/packages/ui/src/components/GroupSettings/GroupSettings.tsx @@ -2,14 +2,18 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import { useState, useEffect } from "react"; +import { useLocation } from "wouter"; import { mqttStateToBoolean, booleanToMqttState, updateGroupState, + percentage, } from "../../utils/deviceUtilities"; -import LoadingSpinner from "../LoadingSpinner/LoadingSpinner"; +import Button from "../Button/Button"; import EditableText from "../EditableText/EditableText"; +import LoadingSpinner from "../LoadingSpinner/LoadingSpinner"; +import Slider from "../Slider/Slider"; import Toggle from "../Toggle/Toggle"; const backend = import.meta.env.VITE_API_URL ?? ""; @@ -26,15 +30,36 @@ function GroupSettings(props) { .then((data) => setGroupSettingsState(data.data)); }; + const [location, navigate] = useLocation(); + useEffect(() => { fetchGroupState(); }, []); + const deleteButton = ( +