Nodes

The Node Manager is a central component that manages all the nodes in your hydroponic system.

Nodes are individual components that perform specific tasks within the hydroponic system. They can have sensors, actuators, or controllers.

All applications need a NodeManager

There are currently two implementations of the NodeManager and their APIs are interoperable. The NodeManager in the openhydroponics.net modules offer direct access to the CANbus and can be used to send low level messages to the network. The other NodeManager in the openhydroponics.dbus module and offers a faster and more lightweight implementation. Instead of raw communication it connects to the openhydroponicd daemon over d-bus. dbus.NodeManager is the preffered implementation to use in third party applications.

Example:

import asyncio

from openhydroponics.dbus import NodeManager


async def main():
    nm = NodeManager()
    await nm.init()
    async for node in nm:
        print(f"Node {node.uuid}")

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass

Getting a reference to a node object use the function NodeManager.get_node() or NodeManager.request_node().

Endpoints

All nodes have one or several endpoints. A node that supports temperature and humidity may have two endpoints, one for each sensor. If a node supports multiple variants of the same type of sensor or actuators each individual sensor/actuator will have its own endpoint. Example: A node with 3 pump outlets will have three endpoints, one for each pump.

API reference

class openhydroponics.base.NodeManagerBase
add_node(node_id: Any, node: Node)

Add a new node to the manager. This method is only meant to be called by a subclass implementing the NodeManager interface.

Parameters:
  • node_id (Any) – The unique identifier for the node. What this is depends on the implementation.

  • node (Node) – The node instance to be added

Raises:

ValueError: If a node with the given ID already exists

get_node(uuid: str | UUID) Node | None

Retrieves a node by its UUID.

Param:

uuid (Union[str, UUID]): The UUID of the node to retrieve, either as a string or a UUID object.

Returns:

Union[NodeType, None]: The node with the specified UUID if found, None otherwise.

get_node_by_id(node_id: Any) Node | None

Retrieves a node by its internal ID. This is typically used for internal management and is not meant to be used by external code. This method is only meant to be called by a subclass implementing the NodeManager interface.

Parameters:

node_id (Any) – The internal ID of the node to retrieve.

Returns:

Union[NodeType, None]: The node with the given ID if found, or None if not found.

abstractmethod async init()

Initialize the node manager.

This asynchronous method performs initialization tasks for the node manager. It should be called once after the node manager instance is created.

Returns:

None

async nodes() AsyncGenerator[Node, None]

Asynchronously yields all registered nodes.

If no nodes are currently available, this method will wait for 1 second to allow for potential heartbeats from nodes to be received before continuing.

Yields:

Each node registered with the manager.

on_node_added(node: Node)

Signal emitted when a node is added to the manager.

Parameters:

node – The node that was added.

on_node_endpoint_value_changed(node: Node, endpoint: Endpoint, value: float, scale: int)

Signal emitted when the value of a node’s endpoint changes.

Parameters:
  • node – The identifier of the node where the change occurred.

  • endpoint – The specific endpoint of the node that changed.

  • value – The new value of the endpoint.

  • scale – The scale or unit associated with the value.

async request_node(uuid: str | UUID, timeout_s: float = 2.0) Node | None

Attempts to retrieve a node with the specified UUID, if the node is not found it will wait for the node to be available for a specified timeout period. This method is useful for ensuring that the node is available before proceeding with further operations.

Parameters:
  • uuid (str | UUID) – The UUID of the node to retrieve, either as a string or UUID object.

  • timeout_s (float) – The maximum time in seconds to wait for the node to be available. Defaults to 2.0 seconds.

Returns:

Node | None: The node with the specified UUID if found within the timeout period, None otherwise.

class openhydroponics.base.NodeBase(uuid: UUID)
add_endpoint(endpoint_no: int, instance: Endpoint)

Adds an endpoint to this node and sets up its value change tracking. This method is meant to be called by the node subclass when a new endpoint is added to the node.

Parameters:
  • endpoint_no (int) – A unique identifier for the endpoint within this node.

  • instance (Endpoint) – The endpoint instance to be added.

property endpoints: Dict[int, Endpoint]

Returns the endpoints collection of this node.

Returns:

The collection of endpoint objects registered with this node.

get_endpoint(endpoint_id: int) Endpoint | None

Retrieves an endpoint by its ID.

Parameters:

endpoint_id (int) – The ID of the endpoint to retrieve.

Returns:

The endpoint with the specified ID if it exists, None otherwise.

get_endpoint_value(endpoint_id: int) Tuple[float, int]

Retrieves the value and scale of a specific endpoint by its ID.

Parameters:

endpoint_id – The unique identifier of the endpoint to retrieve.

Returns:

A tuple containing the endpoint value (float or None if endpoint not found) and the endpoint scale (int or None if endpoint not found)

property number_of_endpoints: int

Returns the number of endpoints provided by this node.

Returns:

The number of endpoints that this node exposes.

on_endpoint_added(endpoint: Endpoint)

Signal emitted when a new endpoint is added.

Parameters:

endpoint (Endpoint) – The endpoint object that has been added.

on_endpoint_value_changed(value: float, scale: int)

Signal emitted when an endpoint’s value changes.

Parameters:
  • endpoint (Endpoint) – The endpoint whose value has changed.

  • value (float) – The new value of the endpoint.

  • scale (int) – The scaling factor or unit for the value.

property uuid: UUID

Returns the UUID of the node.

class openhydroponics.base.Endpoint(node, endpoint_id: int)
ENDPOINT_CLASS = 0
property endpoint_id: int

Get the endpoint number for this endpoint.

Returns:

The endpoint’s identifier that distinguishes it from other endpoints in this node.

async interview()

Conducts an interactive interview process for endpoint configuration.

This asynchronous method prompts for and collects necessary information to set up or configure the endpoint.

property node

Returns the node associated with this endpoint.

on_value_changed(value: float, scale: int)

Signal emitted when a sensor reading is received.

Parameters:
  • value – The value of the sensor reading.

  • scale – The scale of the sensor reading.

async set_config(config: dict[str, Any])

Set configuration parameters for this endpoint.

This method updates the endpoint’s configuration with the provided dictionary of settings.

Parameters:

config – A dictionary containing configuration parameters where keys are parameter names and values are the parameter values. This can include settings like thresholds, calibration values, or other endpoint-specific configurations.

class openhydroponics.net.NodeManager(phy_iface: CanPhyIface = None)
add_node(node_id: Any, node: Node)

Add a new node to the manager. This method is only meant to be called by a subclass implementing the NodeManager interface.

Parameters:
  • node_id (Any) – The unique identifier for the node. What this is depends on the implementation.

  • node (Node) – The node instance to be added

Raises:

ValueError: If a node with the given ID already exists

get_node(uuid: str | UUID) Node | None

Retrieves a node by its UUID.

Param:

uuid (Union[str, UUID]): The UUID of the node to retrieve, either as a string or a UUID object.

Returns:

Union[NodeType, None]: The node with the specified UUID if found, None otherwise.

get_node_by_id(node_id: Any) Node | None

Retrieves a node by its internal ID. This is typically used for internal management and is not meant to be used by external code. This method is only meant to be called by a subclass implementing the NodeManager interface.

Parameters:

node_id (Any) – The internal ID of the node to retrieve.

Returns:

Union[NodeType, None]: The node with the given ID if found, or None if not found.

async init()

Initialize the node manager.

This asynchronous method performs initialization tasks for the node manager. It should be called once after the node manager instance is created.

Returns:

None

property node_id: int

Get the node id the manager is using on the CANbus.

async nodes() AsyncGenerator[Node, None]

Asynchronously yields all registered nodes.

If no nodes are currently available, this method will wait for 1 second to allow for potential heartbeats from nodes to be received before continuing.

Yields:

Each node registered with the manager.

async request_node(uuid: str | UUID, timeout_s: float = 2.0) Node | None

Attempts to retrieve a node with the specified UUID, if the node is not found it will wait for the node to be available for a specified timeout period. This method is useful for ensuring that the node is available before proceeding with further operations.

Parameters:
  • uuid (str | UUID) – The UUID of the node to retrieve, either as a string or UUID object.

  • timeout_s (float) – The maximum time in seconds to wait for the node to be available. Defaults to 2.0 seconds.

Returns:

Node | None: The node with the specified UUID if found within the timeout period, None otherwise.

class openhydroponics.net.Node(node_id: int, uuid: UUID, manager: NodeManagerBase, phy_iface: CanPhyIface)
add_endpoint(endpoint_no: int, instance: Endpoint)

Adds an endpoint to this node and sets up its value change tracking. This method is meant to be called by the node subclass when a new endpoint is added to the node.

Parameters:
  • endpoint_no (int) – A unique identifier for the endpoint within this node.

  • instance (Endpoint) – The endpoint instance to be added.

property endpoints: Dict[int, Endpoint]

Returns the endpoints collection of this node.

Returns:

The collection of endpoint objects registered with this node.

get_endpoint(endpoint_id: int) Endpoint | None

Retrieves an endpoint by its ID.

Parameters:

endpoint_id (int) – The ID of the endpoint to retrieve.

Returns:

The endpoint with the specified ID if it exists, None otherwise.

get_endpoint_value(endpoint_id: int) Tuple[float, int]

Retrieves the value and scale of a specific endpoint by its ID.

Parameters:

endpoint_id – The unique identifier of the endpoint to retrieve.

Returns:

A tuple containing the endpoint value (float or None if endpoint not found) and the endpoint scale (int or None if endpoint not found)

async interview()

Interview the node to gather information about its endpoints.

This asynchronous method queries the node for its number of endpoints (if not already known), then iterates through each endpoint to gather its information.

This method is called by the NodeManager when the node is first discovered and should normally not be called directly by the user. The interview process involves sending requests to the node and waiting for responses.

property interviewed: bool

Check if all expected endpoints for this node have been interviewed/discovered.

Returns:

True if all expected endpoints have been discovered (number_of_endpoints equals the actual count of endpoints), False otherwise.

property node_id: int

Returns the node id of this node on the CANbus network.

The node_id is an integer that uniquely identifies this node in the network.

Returns:

The unique identifier of this node.

property number_of_endpoints: int

Returns the number of endpoints provided by this node.

Returns:

The number of endpoints that this node exposes.

on_interview_done()

Signal emitted when the interview process is completed. This method is typically used to perform any necessary actions or updates after the interview process has finished successfully.

async send_and_wait(request: Msg)

Send a request message and wait for the corresponding response.

This method sends a request message to the node and waits for the matching response. It automatically determines the expected response type based on the request’s message ID.

Parameters:

request – The request message to send. Must be of MsgType.Request.

Returns:

The response message received.

Raises:
  • AssertionError – If the request is not of MsgType.Request or if no corresponding response message class exists for the request’s message ID.

  • asyncio.TimeoutError – If the response is not received within the specified timeout period.

send_msg(msg: Msg)

Send a message to the node. This method encodes the message using the appropriate arbitration ID and sends it through the physical interface.

Parameters:

msg – The message to send. Must be of type Msg.

send_rtr(msg: Any)

Send a remote transmission request (RTR) to the node.

async set_config(config_no: int, config: Any)

Set the configuration for the node. This method sends a configuration request to the node with the specified configuration number and configuration data.

Parameters:
  • config_no – The configuration number to set.

  • config – The configuration data to send.

property uuid: UUID

Returns the UUID of the node.

async wait_for(msg: Msg)

Wait for a specific message type from the node. This method listens for incoming messages and returns the first message that matches the specified message type.

Parameters:

msg – The message type to wait for. Must be of type Msg.

Returns:

The received message of the specified type.

Raises:

asyncio.TimeoutError – If no matching message is received within the specified timeout period.

class openhydroponics.dbus.NodeManager(bus_type: BusType = BusType.SYSTEM)
add_node(node_id: Any, node: Node)

Add a new node to the manager. This method is only meant to be called by a subclass implementing the NodeManager interface.

Parameters:
  • node_id (Any) – The unique identifier for the node. What this is depends on the implementation.

  • node (Node) – The node instance to be added

Raises:

ValueError: If a node with the given ID already exists

get_node(uuid: str | UUID) Node | None

Retrieves a node by its UUID.

Param:

uuid (Union[str, UUID]): The UUID of the node to retrieve, either as a string or a UUID object.

Returns:

Union[NodeType, None]: The node with the specified UUID if found, None otherwise.

get_node_by_id(node_id: Any) Node | None

Retrieves a node by its internal ID. This is typically used for internal management and is not meant to be used by external code. This method is only meant to be called by a subclass implementing the NodeManager interface.

Parameters:

node_id (Any) – The internal ID of the node to retrieve.

Returns:

Union[NodeType, None]: The node with the given ID if found, or None if not found.

async init()

Initialize the node manager.

This asynchronous method performs initialization tasks for the node manager. It should be called once after the node manager instance is created.

Returns:

None

async nodes() AsyncGenerator[Node, None]

Asynchronously yields all registered nodes.

If no nodes are currently available, this method will wait for 1 second to allow for potential heartbeats from nodes to be received before continuing.

Yields:

Each node registered with the manager.

async request_node(uuid: str | UUID, timeout_s: float = 2.0) Node | None

Attempts to retrieve a node with the specified UUID, if the node is not found it will wait for the node to be available for a specified timeout period. This method is useful for ensuring that the node is available before proceeding with further operations.

Parameters:
  • uuid (str | UUID) – The UUID of the node to retrieve, either as a string or UUID object.

  • timeout_s (float) – The maximum time in seconds to wait for the node to be available. Defaults to 2.0 seconds.

Returns:

Node | None: The node with the specified UUID if found within the timeout period, None otherwise.

class openhydroponics.dbus.Node(uuid: UUID, proxy_object: ProxyObject)
add_endpoint(endpoint_no: int, instance: Endpoint)

Adds an endpoint to this node and sets up its value change tracking. This method is meant to be called by the node subclass when a new endpoint is added to the node.

Parameters:
  • endpoint_no (int) – A unique identifier for the endpoint within this node.

  • instance (Endpoint) – The endpoint instance to be added.

property endpoints: Dict[int, Endpoint]

Returns the endpoints collection of this node.

Returns:

The collection of endpoint objects registered with this node.

get_endpoint(endpoint_id: int) Endpoint | None

Retrieves an endpoint by its ID.

Parameters:

endpoint_id (int) – The ID of the endpoint to retrieve.

Returns:

The endpoint with the specified ID if it exists, None otherwise.

get_endpoint_value(endpoint_id: int) Tuple[float, int]

Retrieves the value and scale of a specific endpoint by its ID.

Parameters:

endpoint_id – The unique identifier of the endpoint to retrieve.

Returns:

A tuple containing the endpoint value (float or None if endpoint not found) and the endpoint scale (int or None if endpoint not found)

async init(bus: MessageBus, introspection)
property number_of_endpoints: int

Returns the number of endpoints provided by this node.

Returns:

The number of endpoints that this node exposes.

property uuid: UUID

Returns the UUID of the node.