You can now develop widgets or interfaces that look and work like Ubidots, whether it's to save time in the development process or to ensure that those components blend in with the rest of the platform. This is possible thanks to Vulcan, our home-developed design system that centers on being clean and functional by going from atoms to particles.
Over the past months we’ve been gradually introducing Vulcan into Ubidots to update different pages and components across the platform and now we’re making it available for your use. Learn how to in this guide.
Requirements
Ubidots account (trial or licensed).
1. How to use Vulcan
Using Vulcan within Ubidots is as easy as going into this documentation to find the component you’d like to use and then invoke it in your code. Currently, Vulcan can be used in the HTML Canvas by enabling the use of React in the widget’s configuration. It will be possible to use Vulcan in other parts of Ubidots later on.
If you’d like to use Vulcan in your personal projects, please contact support at support@ubidots.com.
2. Example of Vulcan used in an HTML Canvas
In this example, we’ve used Vulcan to develop an HTML Canvas widget that allows us to send values and context selected from a list to a variable.
Go to a dashboard and create an HTML Canvas widget.
In its configuration, enable React.js.
Click on the “edit code” field next to “code editor”.
Take the following code and paste it into the “JavaScript (JSX)” tab of the editor.
const { Button, VulcanUIProvider, Switch, Text, Dropdown, Input, createTheme, InputCombo, Flex } = VulcanUI
const { Suspense, useState } = React
const $root = document.getElementById("root");
function Spinner(){
return <div className="spinner-container"><div className="lds-grid"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div></div>
}
const root = ReactDOM.createRoot($root)
root.render(
<Suspense fallback={<Spinner />}>
<App />
</Suspense>
)
const axiosInstance = axios.create({
baseURL: "https://industrial.api.ubidots.com/api",
headers: {
"X-Auth-Token": "TOKEN-HERE",
"Content-Type": "application/json",
Authorization: "Bearer TOKEN-HERE",
},
});
function createResource(promise) {
const state = { status: "loading", data: null, error: null };
let response = promise
.then((data) => {
state.status = "success";
state.data = data;
state.error = null;
})
.catch((error) => {
state.status = "error";
state.error = error;
});
return {
get() {
if (state.status === "loading") throw response;
if (state.status === "error") throw state.error;
if (state.status === "success") return state.data;
throw new Error("The request failed, please try again.");
},
};
}
async function getDevice(id) {
const { data } = await axiosInstance.get(`/v2.0/devices/${id}`);
return data;
}
async function getVariables(id) {
const { data } = await axiosInstance.get(`/v2.0/devices/${id}/variables`);
return data.results;
}
async function sendDot(deviceLabel, dot) {
await axiosInstance.post(`/v1.6/devices/${deviceLabel}`, dot);
}
const deviceResource = createResource(getDevice("DEVICE-ID-HERE"));
const variablesResource = createResource(
getVariables("DEVICE-ID-HERE")
);
const theme = createTheme({});
const contextOptions = [
{
label: "Healthy",
value: "healthy",
},
{
label: "Operational",
value: "operational",
},
{
label: "Degraded",
value: "degraded",
},
{
label: "Maintenance",
value: "maintenance",
},
{
label: "Damaged",
value: "damaged",
}
];
function App() {
const device = deviceResource.get();
const variables = variablesResource.get();
const [selectedVariable, setSelectedVariable] = useState(null);
const [value, setValue] = useState("");
const [contextValue, setContextValue] = useState("");
const [loading, setLoading] = useState(false);
const sendDotHandler = async () => {
if (!selectedVariable) return alert("Select a variable");
if (!value) return alert("Enter a value");
if (!contextValue) return alert("Enter a context value");
try {
setLoading(true);
const dot = {
[selectedVariable?.label]: {
value: Number(value),
context: {
"status": contextValue.value,
},
},
};
await sendDot(device.label, dot);
} finally {
setLoading(false);
}
};
return (
<VulcanUIProvider theme={theme} injectFonts>
<div className="container">
<Text as="h1">{device.name}</Text>
<InputCombo
label="Variable"
>
{({ id }) => (
<Dropdown onSelect={setSelectedVariable} placement="bottom-start">
<Dropdown.Trigger fullWidth id={id}>
{selectedVariable?.name ?? "- Select variable -"}
</Dropdown.Trigger>
<Dropdown.Menu>
{variables.map((variable) => (
<Dropdown.Item key={variable.id} value={variable}>
{variable.name}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
)}
</InputCombo>
<InputCombo label="Value">
<Input
startContent="Value:"
type="number"
value={value}
onValueChange={setValue}
placeholder="Enter value"
fullWidth
/>
</InputCombo>
<InputCombo
label="Status"
description={`Select the device status`}
>
{({ id }) => (
<Dropdown onSelect={setContextValue} placement="bottom-start">
<Dropdown.Trigger fullWidth id={id}>
{contextValue?.label ?? "- Select context -"}
</Dropdown.Trigger>
<Dropdown.Menu>
{contextOptions.map((option) => (
<Dropdown.Item key={option.value} value={option}>
{option.label}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
)}
</InputCombo>
<br />
<Button disabled={loading} onPress={sendDotHandler} color="success">
Send
</Button>
</div>
</VulcanUIProvider>
);
}
Note:
In the code, replace the “TOKEN-HERE” texts with your token.
Also replace the “DEVICE-ID-HERE” with the ID of the device you’d like to send values to.