- {props.devices.map((device) => (
+ {props.devices.map((device: Device) => (
props.onClick} />
diff --git a/frontend/src/components/DeviceSettings/DeviceSettings.tsx b/frontend/src/components/DeviceSettings/DeviceSettings.tsx
index fe90c4b..e4a4437 100644
--- a/frontend/src/components/DeviceSettings/DeviceSettings.tsx
+++ b/frontend/src/components/DeviceSettings/DeviceSettings.tsx
@@ -8,6 +8,8 @@ import { deviceSettingsGenerator } from "./generator";
import EditableText from "../EditableText/EditableText";
import { deviceDescription } from "../../utils/deviceUtilities";
+const backend = import.meta.env.VITE_API_URL ?? "";
+
function DeviceSettings(props) {
const [deviceSettingsState, setDeviceSettingsState] = useState();
const [deviceFriendlyNameState, setDeviceFriendlyNameState] = useState(
@@ -26,9 +28,9 @@ function DeviceSettings(props) {
properties[property.name] = "";
});
- getDeviceSettings(props.device.friendly_name, properties).then(
- setDeviceSettingsState,
- );
+ fetch(`${backend}/api/devices/${props.device.ieee_address}/state`)
+ .then((res) => res.json())
+ .then((data) => setDeviceSettingsState(data.data));
}, []);
if (!deviceSettingsState) return ;
diff --git a/frontend/src/components/DeviceSettings/generator.tsx b/frontend/src/components/DeviceSettings/generator.tsx
index a4b62cc..b8e4c44 100644
--- a/frontend/src/components/DeviceSettings/generator.tsx
+++ b/frontend/src/components/DeviceSettings/generator.tsx
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
+ mqttStateToBoolean,
booleanToMqttState,
hexToRGB,
hslToRGB,
@@ -10,6 +11,7 @@ import {
} from "../../utils/deviceUtilities";
import { numericTransformer } from "../../utils/transformers";
import Toggle from "../Toggle/Toggle";
+import ColorPicker from "../ColorPicker/ColorPicker";
export const deviceSettingsGenerator = (
device,
@@ -30,9 +32,7 @@ export const deviceSettingsGenerator = (
onChange={(event) => {
const newMqttState = booleanToMqttState(event.target.checked);
updateDeviceState(
- deviceSettingsState,
- setDeviceSettingsState,
- device.friendly_name,
+ device.ieee_address,
feature.name,
newMqttState,
);
diff --git a/frontend/src/components/GroupCard/GroupCard.tsx b/frontend/src/components/GroupCard/GroupCard.tsx
new file mode 100644
index 0000000..0b00aa5
--- /dev/null
+++ b/frontend/src/components/GroupCard/GroupCard.tsx
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: © 2024 Amber Cronin
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+import styled from "styled-components";
+import { StyledText, StyledHeader } from "../../utils/theme";
+
+import { type Group } from "../../../../types/zigbee_types";
+
+const Card = styled.div`
+ margin: 2em 0em;
+ padding: 1em;
+ cursor: pointer;
+ border-radius: 2rem;
+
+ &:hover {
+ background-color: ${({ theme }) => theme.hover};
+ }
+`;
+
+function GroupCard(props: { group: Group; onClick: Function }) {
+ return (
+
+ {props.group.friendly_name}
+ {`${props.group.members.length} devices`}
+
+ );
+}
+
+export default GroupCard;
diff --git a/frontend/src/components/GroupList/GroupList.tsx b/frontend/src/components/GroupList/GroupList.tsx
new file mode 100644
index 0000000..a44c0d9
--- /dev/null
+++ b/frontend/src/components/GroupList/GroupList.tsx
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: © 2024 Amber Cronin
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+import { Link } from "wouter";
+import GroupCard from "../GroupCard/GroupCard";
+import { type Group } from "../../../../types/zigbee_types";
+
+function GroupList(props: { groups: Group[]; onClick?: Function }) {
+ return (
+
+ {props.groups.map((group: Group) => (
+
+ props.onClick} />
+
+ ))}
+
+ );
+}
+
+export default GroupList;
diff --git a/frontend/src/pages/Groups/Groups.tsx b/frontend/src/pages/Groups/Groups.tsx
new file mode 100644
index 0000000..ad66919
--- /dev/null
+++ b/frontend/src/pages/Groups/Groups.tsx
@@ -0,0 +1,23 @@
+// SPDX-FileCopyrightText: © 2024 Amber Cronin
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+import LoadingSpinner from "../../components/LoadingSpinner/LoadingSpinner";
+import GroupList from "../../components/GroupList/GroupList";
+
+function Groups(props) {
+ const selectedGroup = props.selectedGroup;
+
+ let groupContent = undefined;
+
+ if (!props.groups || !selectedGroup) groupContent = ;
+
+ if (selectedGroup) groupContent = ;
+
+ if (props.groups && !selectedGroup) {
+ groupContent = ;
+ }
+
+ return <>{groupContent !== undefined ? groupContent : ""}>;
+}
+
+export default Groups;
diff --git a/frontend/src/utils/deviceUtilities.ts b/frontend/src/utils/deviceUtilities.ts
index 7824f3a..87fb475 100644
--- a/frontend/src/utils/deviceUtilities.ts
+++ b/frontend/src/utils/deviceUtilities.ts
@@ -1,30 +1,36 @@
// SPDX-FileCopyrightText: © 2021 Amber Cronin
// SPDX-License-Identifier: AGPL-3.0-or-later
-export const mqttStateToBoolean = (state) => {
+const backend = import.meta.env.VITE_API_URL ?? "";
+
+export const mqttStateToBoolean = (state: string): boolean => {
if (state === "ON") return true;
return false;
};
-export const booleanToMqttState = (boolean) => {
+export const booleanToMqttState = (boolean: boolean): string => {
if (boolean) return "ON";
return "OFF";
};
export const updateDeviceState = (
- deviceSettingsState,
- setDeviceSettingsState,
- deviceFriendlyName,
- property,
- value,
+ deviceId: string,
+ property: string,
+ value: any,
) => {
- const updateObject = {};
- updateObject[property] = value;
- setDeviceSettings(deviceFriendlyName, updateObject);
-
- const clonedState = { ...deviceSettingsState };
- clonedState[property] = value;
- setDeviceSettingsState(clonedState);
+ const request = new Request(`${backend}/api/devices/${deviceId}/state`, {
+ method: "POST",
+ mode: "cors",
+ headers: {
+ "content-type": "application/json",
+ },
+ body: JSON.stringify({
+ setting: property,
+ value: value,
+ }),
+ });
+
+ fetch(request).then();
};
export const deviceDescription = (deviceDefinition) => {