Skip to content

Tutorial ‐ Connecting Components to Adapters

Ashley Neaves edited this page Sep 6, 2023 · 1 revision

To start with something simple, lets create two components that connect to a specific path within an Adapter's parameter tree.

Let's assume that this adapter is called example has a path within its parameter tree that looks something like this:

"heater"
{
    "active": false
    "temperature": 15
}

active is readable and writeable, setting it to true activates some sort of heating element, causing the temperature to rise. temperature is read only, and displays the current temperature of the heating element in degrees Celsius.

To get access to this part of the parameter tree, we can create an AdapterEndpoint object that points to this adapter.

const heaterEndpoint = useAdapterEndpoint("example", "http://localhost:8888", 1000);

This endpoint object will allow us to see and modify the adapter's parameter tree from within the Odin React App. The 1000 at the end tells it we want to refresh the "local view" of the parameter tree once every 1000 milliseconds, to keep the temperature reading on the GUI updating. Without this, the Endpoint object would only update when a PUT or GET request is made using it.

In order to make components that can interact with this, we need to create a component that is wrapped in the WithEndpoint Custom hook. This hook returns the component provided, but automatically handles events such as the OnClick event method for a button, in order to send information to the endpoint. Let's create an EndpointToggle, a toggle switch that can send something to the endpoint when clicked.

const EndpointToggle = WithEndpoint(ToggleSwitch);

As this is basically declaring a custom component, it should be done just underneath any imports you need, before you declare the Functional Component that makes up the actual App.

Now, within the return method of the Functional Component, we can add the endpoint toggle switch that will change the value of active within that endpoint's parameter tree

return (
    <EndpointToggle endpoint={heaterEndpoint} event_type="click" label="Activate Heater"
     fullpath="heater/active" checked={heaterEndpoint.data.heater?.active || false}/>
...
)

A toggle switch, as defined by the code above Note the question mark notation within the definition for checked. This is called Optional Chaining and is a useful means of accessing properties of an object that are not guaranteed to exist. If the endpoint object is unable to populate it's data object for whatever reason then it will evaluate to undefined and thus the component will instead use the other side of the || OR operator, which in this case if false.

Because of the way the AdapterEndpoint hook is designed, the data object within it will always initiate as an empty object {}, which will be populated once a successful GET request is finished. Because of this, the first render of a component using the AdapterEndpoint data object will not have access to that data, and so there should always be some form of check to stop the app crashing as soon as it attempts its first render.

Once we've got the toggle switch working, we can include something to display the Temperature. Because this is read only, we can simply use the data object from the Adpater Endpoint hook. Lets use a StatusBox component to do this:

...
<StatusBox label="Temperature" type={heaterEndpoint.data.heater?.active ? "success" : "danger"}>
    {heaterEndpoint.data.heater?.temperature || "Unknown"}
</StatusBox>

The Toggle Switch from the previous example, but now with a Red status box, that reads "Temperature: Unknown"

Again, because we are using the data object from the heaterEndpoint hook, we need to ensure there are checks in place to make sure the data exists and is accessible. As you can see in the screenshot, because this Adapter does not actually exist, the Card has been coloured Red (due to the single line IF statement in the type definition) and it currently reads "Unknown".

These are the two main ways you might connect a component with an adapter. It demonstrates the fact that two or more components can make use of the same AdapterEndpoint hook, and you can see that changes made by one component will be reflected in all other components using that same Endpoint.

Clone this wiki locally