Proposal for a Library Dependency System in Hubitat Package Manager (HPM)
This post outlines a proposal for a new library dependency system in Hubitat Package Manager. The goal is to create a more robust and automated way to manage shared code (libraries) across the Hubitat community, addressing the limitations of the current "Bundles" system while leveraging the existing infrastructure.
The Problem: Managing Shared Code in Hubitat
Currently, managing shared code in Hubitat, and in HPM is challenging:
- Manual Process with Bundles: The existing method for distributing libraries in HPM is through Bundles. This process is manual, requiring developers to create
.zipfiles. It does not provide a seamless way to manage updates or dependencies. - Code Duplication and Poor Maintenance: Without a proper dependency management system, developers often resort to copying and pasting shared code. This happens both across the community and within a developer's own collection of drivers. A complex driver system with multiple components can lead to significant internal code duplication, making maintenance difficult and error-prone. On a community level, this leads to inconsistencies, with a prime example being logging frameworks where nearly every developer has their own implementation.
- No Automatic Updates for Libraries: When a developer updates a library, there is no automated way for HPM to detect and install the update for all the drivers and apps that use it.
The Proposal: A True Library Dependency System
I propose the creation of a proper library dependency system within HPM that would automate the installation and updating of shared libraries. This system would be based on extending the existing repository.json format that developers already use.
Key Features
- Decentralized Library Definitions: Developers would define the libraries they publish within their own
repository.jsonfile, alongside their existing packages. - Dependency Declaration: Driver and app developers would declare their library dependencies in their
manifest.jsonfile. - Automatic Resolution and Installation: When a user installs or updates a package, HPM would automatically resolve, download, and install the required libraries.
- Automatic Updates: HPM would be able to check for and install updates to libraries, just as it does for apps and drivers.
Proposed Technical Implementation
1. repository.json Modifications
Developers would add a new libraries array to their existing repository.json file. This array would live at the same level as the packages array and would define the libraries they wish to make available.
Example repository.json:
{
"author": "A. Developer",
"gitHubUrl": "https://github.com/a-developer",
"packages": [
{
"id": "f9e1f5b0-c5e5-4f3c-b8f9-2e1b8a0b1b9e",
"name": "Sample Weather Driver",
"category": "Integrations",
"location": "https://raw.githubusercontent.com/a-developer/hubitat-weather-driver/main/manifest.json",
"description": "A driver for a sample weather device."
}
],
"libraries": [
{
"id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"name": "CommonUtils",
"namespace": "a.developer.libraries.utils",
"version": "1.0.0",
"location": "https://raw.githubusercontent.com/a-developer/hubitat-common-utils/main/CommonUtils.groovy",
"description": "A library of common utility functions."
}
]
}
2. manifest.json Modifications
A new, optional, libraryDependencies array would be added to the packageManifest.json format. To ensure a unique reference, developers would specify both the library name and its namespace.
{
"packageName": "Sample Weather Driver",
// ... other manifest properties
"drivers": [
// ... driver definitions
],
"libraryDependencies": [
{
"name": "CommonUtils",
"namespace": "a.developer.libraries.utils"
}
]
}
Example Workflow
- Library Developer:
- Creates a new utility library.
- Adds a
librariessection to theirrepository.jsonfile and pushes the change to their GitHub.
- Driver Developer:
- In their driver's
manifest.json, adds a dependency on the library by specifying itsnameandnamespace. - In their driver's code, uses
#include a.developer.libraries.utils.CommonUtils.
- In their driver's
- End User:
- Installs the driver via HPM.
- HPM automatically looks up
CommonUtilsusing its unique namespace and name combination, finds its location, and installs it. When the library developer updatesCommonUtils, HPM notifies the user of the update during its next check.
Edit: That wouldn't work, libraries would have to be versioned and tagged. Drivers would declare a specific, immutable version.
Benefits
- Reduces Code Duplication: Encourages the use of shared, community-vetted libraries.
- Improves Code Quality: Allows for the creation of standardized libraries for common tasks like logging, data parsing, and device communication.
- Simplifies Development: Developers can focus on their app or driver's unique logic instead of reinventing the wheel.
- Enhances User Experience: Users get a more robust and reliable system with automatic updates for all components.
- Supports Both Public and "Private" Libraries: A library dependency doesn't necessarily need to be for community-wide use, like a logging library. It can also be a private utility library that a developer uses to reduce code duplication across their own complex driver system, but which is not intended for general consumption by other developers.
- Decentralized and Scalable: Developers can publish and update libraries without needing to submit a PR to a central repository for every version change, making the system more scalable and reducing the maintenance burden on the HPM team.
Initial Scope and Future Considerations
For the initial implementation, I propose that we do not support multiple versions of the same library. New versions of a library would be expected to be backward-compatible, if they intend do be widely used. This simplifies the initial development effort and aligns with the current HPM philosophy.
This proposal aims to start a conversation about improving library management in the Hubitat ecosystem. I believe that a proper dependency system would be a significant step forward for both developers and users.