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

NodeManager

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.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.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.

Nodes

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.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.

async get_config(endpoint_id: int, config_no: int) bytes

Get the configuration for an endpoint in the node. This method sends a request to the node to retrieve the configuration for the specified endpoint.

Parameters:
  • endpoint_id – The id of the endpoint to get the configuration for.

  • config_no – The configuration number to retrieve.

Returns:

The configuration data received from the 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(endpoint_id: int, config_no: int, config: Any) bool

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

Parameters:
  • endpoint_id – The id of the endpoint to configure.

  • 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.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.

Endpoints

class openhydroponics.base.endpoint.ECConfigReadType(*values)
class openhydroponics.base.endpoint.ECConfigWriteType(*values)
class openhydroponics.base.endpoint.ECEndpoint(node, endpoint_id)
class openhydroponics.base.endpoint.Endpoint(node, endpoint_id: int)

Base class for all endpoints. This class provides a common interface for all endpoint types.

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 get_config(config: int) dict[str, Any]

Get configuration parameters for this endpoint.

This method retrieves the current configuration settings for the endpoint.

Parameters:

config – The configuration number to retrieve.

Returns:

The value of the requested configuration parameter.

async init()

Initialize the endpoint.

This method is called to set up the endpoint after it has been created. It can be used to perform any necessary setup or configuration.

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.

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

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.

Returns:

True if the configuration was successfully set, False otherwise.

class openhydroponics.base.endpoint.EndpointClass(*values)

Endpoint class types. These classes are used to categorize the type of endpoint in a node.

Input = 1

Input endpoint

NotSupported = 0

Not supported

Output = 2

Output endpoint

class openhydroponics.base.endpoint.EndpointInputClass(*values)

Endpoint input class types. These classes are used to categorize the type of input endpoint in a node.

EC = 3

Electrical Conductivity endpoint

Humidity = 2

Humidity endpoint

NotSupported = 0

Not supported

PH = 4

pH endpoint

Temperature = 1

Temperature endpoint

class openhydroponics.base.endpoint.EndpointOutputClass(*values)

Endpoint output class types. These classes are used to categorize the type of output endpoint in a node.

Binary = 2

Binary output endpoint

NotSupported = 0

Not supported

Variable = 1

Variable output endpoint

class openhydroponics.base.endpoint.HumidityEndpoint(node, endpoint_id)
class openhydroponics.base.endpoint.InputEndpoint(node, endpoint_id)
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.

update(value: float, scale: int)

Update the endpoint with a new sensor reading.

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

  • scale – The scale of the sensor reading.

class openhydroponics.base.endpoint.OutputEndpoint(node, endpoint_id: int)
class openhydroponics.base.endpoint.PHConfigReadType(*values)
class openhydroponics.base.endpoint.PHConfigWriteType(*values)
class openhydroponics.base.endpoint.PHEndpoint(node, endpoint_id)
class openhydroponics.base.endpoint.TemperatureEndpoint(node, endpoint_id)
class openhydroponics.base.endpoint.VariableOutputEndpoint(node, endpoint_id)
on_value_changed(value: float)

Signal emitted when a new output value is set. :param value: The new output value.

async set(value: float)

Set the output value for this endpoint.

Parameters:

value – The value to set for the output.

update(value: float)

Update the endpoint with a new output value.

Parameters:

value – The value of the output.

property value

Get the current output value for this endpoint.

Returns:

The current output value.