Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

build/

externals/
4 changes: 2 additions & 2 deletions applications/dev_ui/dev_gui/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Developer GUI install
find_program(YARN_EXECUTABLE "yarn" REQUIRED)
find_program(YARN_EXECUTABLE "yarnpkg" "yarn" REQUIRED)

if(APPLE)
set(NPM_TARGET macos-x64)
Expand Down Expand Up @@ -27,7 +27,7 @@ add_custom_command(
COMMAND cp -r ${ZAP_CURRENT_OUTPUT_DIR}/. .
COMMAND ${YARN_EXECUTABLE} version --no-git-tag-version --new-version
${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}
COMMAND ${YARN_EXECUTABLE} install --allow-same-version
COMMAND ${YARN_EXECUTABLE} install
COMMAND ${YARN_EXECUTABLE} run build
USES_TERMINAL)

Expand Down
22 changes: 22 additions & 0 deletions applications/dev_ui/dev_gui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import Scene from './pages/scenes/scene/scene';
import EpScenes from './pages/scenes/ep-scenes/ep-scenes';
import { CommissionableDevices } from './pages/commissionable-devices/commissionable-devices';
import { Button, Modal, Spinner } from 'react-bootstrap';
import UserCredential from './pages/user-credential/user-credential';

class App extends Component<{}, AppState> {
constructor(props: {}) {
Expand Down Expand Up @@ -148,6 +149,23 @@ class App extends Component<{}, AppState> {
this.setState({ CommissionableDevices: list });
}

handleEvents(data: any) {
var toastType = toast.TYPE.DEFAULT;
switch (data.level) {
case 1:
toastType = toast.TYPE.INFO;
break;
case 2:
toastType = toast.TYPE.WARNING;
break;
case 3:
case 4:
toastType = toast.TYPE.ERROR;
break;
}
toast(data.message, { type: toastType })
}

handleUPTIChange(list: any[]) {
let upti = this.state.UPTI;
upti.List = list;
Expand Down Expand Up @@ -278,6 +296,7 @@ class App extends Component<{}, AppState> {
<Route path='/networkmanagement' exact render={() => <NetworkManagement ref={this.changeNodes} {...baseProps} NodeList={this.state.NodeList} />} />
<Route path='/measurements' exact render={() => <Measurements {...baseProps} NodeList={this.state.NodeList} />} />
<Route path='/commissionabledevices' exact render={() => <CommissionableDevices {...baseProps} List={this.state.CommissionableDevices} />} />
<Route path='/usercredential' exact render={() => <UserCredential ref={this.changeConfParams} {...baseProps} NodeList={this.state.NodeList} />} />
<Redirect from="/" exact to="/nodes" />
{Object.keys(ClusterTypes).map((type, index) =>
<Route key={index} path={NavbarItems.find(i => i.name === type)?.path} render={() =>
Expand Down Expand Up @@ -412,6 +431,9 @@ class App extends Component<{}, AppState> {
case "commissionable-device":
this.handleCommissionableDevices(mes.data);
break;
case "event":
this.handleEvents(mes.data);
break;
}
}

Expand Down
14 changes: 14 additions & 0 deletions applications/dev_ui/dev_gui/src/dev-gui-api/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ exports.processSmartStart = (topic, message) => {
}
}

exports.processEvent = (topic, message) => {
console.log(`Received Event : '${message}'`);
if (message.toString() == "")
return;
try {
return {
type: "event",
data: JSON.parse(message)
};
} catch (error) {
return getErrorResponse(topic, message, error);
}
}

exports.processCluster = (topic, message) => {
let index = -1;
let match = topic.match(/ucl\/by-unid\/(.*)\/(ep\d+)\/(.*)/)
Expand Down
5 changes: 4 additions & 1 deletion applications/dev_ui/dev_gui/src/dev-gui-api/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ let topics = ["ucl/SmartStart/List",
"ucl/UPTICap/#",
"ucl/by-machine-id/+/SystemMetrics/SupportedCommands",
"ucl/by-machine-id/+/SystemMetrics/Attributes/#",
"ucl/by-mqtt-client/+/ApplicationMonitoring/Attributes/#"
"ucl/by-mqtt-client/+/ApplicationMonitoring/Attributes/#",
"ucl/Event"
];
Object.keys(supportedClusters).forEach((i) => {
topics.push(`ucl/by-unid/+/+/${i}/SupportedCommands`);
Expand Down Expand Up @@ -282,6 +283,8 @@ function onMqttMessage(topic, message) {
response = handler.processAppMonitoringList(topic, message);
} else if (topic.match(/ucl\/SmartStart\/CommissionableDevice\/(.*)/)) {
response = handler.processCommissionableDevices(topic, message);
} else if (topic === "ucl/Event") {
response = handler.processEvent(topic, message);
}
if (response && Object.keys(response).length > 0)
handler.addToQueue(response.type, response.data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ClusterTypes } from '../../cluster-types/cluster-types';
import { ClusterViewOverride } from './base-cluster-types';
import { Link } from 'react-router-dom';
import { Tooltip } from '@mui/material';
import { Button } from 'react-bootstrap';
import { Button, Badge } from 'react-bootstrap';

//Here you can find icons that can be used to customize you page: https://react-icons.github.io/react-icons/
//Don`t forgot to check licence if you use something that is not in Licence.txt
Expand Down Expand Up @@ -769,4 +769,23 @@ export let ClusterViewOverrides = {
} as NavbarItem,
IsExpandable: true
} as ClusterViewOverride,

UserCredential: {
NodesTooltip: (endpoint: string) =>
<Tooltip title={`Endpoint ${endpoint}: User Credential`}>
<span className="cursor-default">
<Link to={`/usercredential`}>
<RiIcons.RiUserSettingsFill color="#212529" />
</Link>
</span>
</Tooltip>,
NavbarItem: {
name: "User Credential",
title: 'User Credential',
path: '/usercredential',
icon: <RiIcons.RiUserSettingsFill />,
cName: 'nav-text',
subMenu: SideMenu.Actuators
} as NavbarItem
} as ClusterViewOverride,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type UserDlgState = {
Command: any,
Unid: any,
ShowModal: boolean,
UserCredential: any
}

export type UserDlgProps = {
SocketServer: WebSocket
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import * as React from 'react';
import { Button, Modal } from 'react-bootstrap';
import { UserDlgProps, UserDlgState } from './cred-dlg-types';
import UserCredentialAttrs from '../user-credential-attrs/user-credential-attrs';


class CredDlg extends React.Component<UserDlgProps, UserDlgState> {
constructor(props: UserDlgProps) {
super(props);
this.sendCommand = this.sendCommand.bind(this);
this.toggleModal = this.toggleModal.bind(this);
this.state = {
Command: {},
Unid: "",
ShowModal: false,
UserCredential: {}
};
this.changeCommandAttrs = React.createRef();
}
changeCommandAttrs: any;

toggleModal(value: boolean) {
this.setState({ ShowModal: value });
}

getDefinedUserIDs(userCredential: any) {
var users = userCredential.User
if (!users) {
return [];
}
return Object.keys(users).map(user_id => {
return { label: user_id, id: parseInt(user_id) }
});
}

getSupportedEnum(enumData: any, supportedEnumField: any) {
if (!supportedEnumField) {
return enumData;
}
return enumData.filter((enumItem:any) => supportedEnumField[enumItem.name]);
}

updateState(unid: string, command: any, showModal: boolean, userCredential: any) {
let updatedCommand = structuredClone(command);

updatedCommand.fields = command.fields.map( (field: any) => {
switch (field.name) {
case "UserUniqueID":
field.values = this.getDefinedUserIDs(userCredential);
if (field.values.length !== 0) {
field.defaultValue = field.values[0];
field.default = field.values[0].id;
}
break;
case "CredentialType":
field.enum = this.getSupportedEnum(field.enum, userCredential.SupportedCredentialTypes?.Reported);
break;
}
return field;
});


updatedCommand.UserList = userCredential.User;
updatedCommand.UserCredential = userCredential;

this.setState({ Unid: unid, Command: updatedCommand, ShowModal: showModal, UserCredential: userCredential },
() => {
this.changeCommandAttrs.current.updateState(this.state.Command)
});

}

sendCommand() {
if (this.state.UserCredential !== undefined)
this.props.SocketServer.send(JSON.stringify(
{
type: "run-cluster-command",
data: {
Unid: this.state.Unid,
ClusterType: "UserCredential",
Cmd: this.state.Command.name,
Payload: this.changeCommandAttrs.current.state.Payload
}
}));
}


render() {
return (
<Modal show={this.state.ShowModal} size="lg" onHide={() => this.toggleModal(false)}>
<Modal.Header closeButton>
<Modal.Title>{this.state.Command.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<UserCredentialAttrs ref={this.changeCommandAttrs} />
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={() => { this.sendCommand(); this.toggleModal(false); }}>
Send
</Button>
<Button variant="outline-primary" onClick={() => this.toggleModal(false)}>
Cancel
</Button>
</Modal.Footer>
</Modal>
);
}
}

export default CredDlg
Loading