From 1814e38efcaf9565b292007db8ebcfab553a8bfc Mon Sep 17 00:00:00 2001 From: jack3308 Date: Sun, 29 Dec 2024 18:18:43 +1100 Subject: [PATCH] Restart development from scratch with better development plan and structure. --- .cursorrules | 57 ++++++ README.MD | 31 ---- configuration.yaml | 15 -- custom_components/stockpile/__init__.py | 71 ++++++-- custom_components/stockpile/config_flow.py | 29 --- custom_components/stockpile/const.py | 14 +- custom_components/stockpile/manifest.json | 10 +- custom_components/stockpile/number.py | 51 ------ custom_components/stockpile/sensor.py | 130 +++++++++++++ custom_components/stockpile/services.yaml | 3 - dev_plan.md | 201 ++++++++++++++++++++ hacs.json | 8 - instruction_set.md | 202 +++++++++++++++++++++ sample_config | 8 + 14 files changed, 667 insertions(+), 163 deletions(-) create mode 100644 .cursorrules delete mode 100644 README.MD delete mode 100644 configuration.yaml delete mode 100644 custom_components/stockpile/config_flow.py delete mode 100644 custom_components/stockpile/number.py create mode 100644 custom_components/stockpile/sensor.py delete mode 100644 custom_components/stockpile/services.yaml create mode 100644 dev_plan.md delete mode 100644 hacs.json create mode 100644 instruction_set.md create mode 100644 sample_config diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..609369d --- /dev/null +++ b/.cursorrules @@ -0,0 +1,57 @@ +# Home Assistant Integration Development CursorRules + +# General Python Best Practices +- Use type annotations for all functions and methods. +- Follow PEP 8 for code style and formatting. +- Use docstrings to document all public classes and methods. +- Prefer list comprehensions and generator expressions for creating lists and iterators. + +# Home Assistant Specific Guidelines +- Ensure all integrations are structured according to the Home Assistant architecture. +- Use the `hass` object for accessing core Home Assistant functionalities. +- Define entities using the `Entity` class or its derivatives. +- Implement config entries for user configuration. +- Use the `async` keyword for I/O-bound operations to ensure non-blocking behavior. +- Follow the Integration Quality Scale to ensure high-quality integrations. + +# Code Structure +- Organize code into modules: `__init__.py`, `sensor.py`, `binary_sensor.py`, etc. +- Use constants for configuration keys and default values. +- Separate logic into helper functions or classes to improve readability and maintainability. + +# Error Handling +- Use Python's built-in exceptions for error handling. +- Log errors and warnings using Home Assistant's logging framework. + +# Testing +- Write unit tests for all new features and bug fixes. +- Use Home Assistant's test utilities for setting up test environments. + +# Documentation +- Provide clear and concise documentation for the integration. +- Include examples of configuration and usage in the README.md file. + +# Contribution Guidelines +- Ensure all contributions are original or properly credited. +- Follow the repository's contribution guidelines for pull requests. + +# Feature Scope and Requirements +- Implement only the features that are explicitly requested or required. +- Avoid adding unnecessary features that could complicate the integration. +- Prioritize maintaining simplicity and clarity in the codebase. + +# Beginner-Friendly Guidelines +- Provide clear explanations and comments for all code outputs. +- Document the purpose and usage of each function and class. +- Use simple and straightforward methods whenever possible. +- Avoid complex patterns unless absolutely necessary, and explain them thoroughly if used. + +# Entity-Specific Guidelines +- Use the most appropriate class, service, method, or action for the entity type in question. +- Ensure that the entity's functionality aligns with Home Assistant's core capabilities and standards. +- Regularly review Home Assistant's documentation for updates on best practices and new features. + +# Additional Resources +- Refer to the Home Assistant developer documentation for detailed guidelines: https://developers.home-assistant.io/docs/development_index/ \ +- Refer to 'dev_plan.md' for the overarching project plan +- Refer to 'instruction_set.md' for project specific instructions. \ No newline at end of file diff --git a/README.MD b/README.MD deleted file mode 100644 index dee1f2e..0000000 --- a/README.MD +++ /dev/null @@ -1,31 +0,0 @@ -# StockPile for Home Assistant - -StockPile is a Home Assistant integration that helps you track inventory levels of household items. - -## Features - -- Sensor platform to monitor current stock levels -- Number platform to adjust and update quantities -- Simple setup and configuration - -## Installation - -1. Add this repository to HACS: - - Open HACS in Home Assistant - - Click the three dots in the top right corner - - Select "Custom repositories" - - Add the URL of this repository - - Select "Integration" as the category - -2. Install the integration through HACS: - - Click "Integrations" in HACS - - Click the "+ Explore & Download Repositories" button - - Search for "StockPile" - - Click Download - -3. Restart Home Assistant - -## Configuration - -Add the following to your `configuration.yaml`: - diff --git a/configuration.yaml b/configuration.yaml deleted file mode 100644 index 34e2617..0000000 --- a/configuration.yaml +++ /dev/null @@ -1,15 +0,0 @@ -stockpile: - items: - - name: Toilet Paper - initial_quantity: 12 - min_threshold: 4 - unit_of_measurement: "rolls" - consume_unit: 1 - stock_unit: 12 - - - name: Paper Towels - initial_quantity: 4 - min_threshold: 2 - unit_of_measurement: "rolls" - consume_unit: 1 - stock_unit: 6 \ No newline at end of file diff --git a/custom_components/stockpile/__init__.py b/custom_components/stockpile/__init__.py index aa29c14..3cdd68e 100644 --- a/custom_components/stockpile/__init__.py +++ b/custom_components/stockpile/__init__.py @@ -1,27 +1,60 @@ """The StockPile integration.""" -from homeassistant.core import HomeAssistant, ServiceCall -from homeassistant.config_entries import ConfigEntry +from __future__ import annotations + +import logging +from typing import Any + +import voluptuous as vol + from homeassistant.const import Platform -from homeassistant.helpers.reload import async_setup_reload_service +from homeassistant.core import HomeAssistant +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType + +_LOGGER = logging.getLogger(__name__) + +# Define the schema for each stockpile entry +STOCKPILE_SCHEMA = vol.Schema({ + vol.Required("name"): cv.string, + vol.Required("unit"): cv.string, + vol.Required("initial_quantity"): cv.positive_float, + vol.Optional("min_quantity", default=0): cv.positive_float, + vol.Optional("max_quantity"): cv.positive_float, + vol.Optional("step_size", default=1): cv.positive_float, +}) + +# Define the main configuration schema +CONFIG_SCHEMA = vol.Schema({ + vol.Required("stockpile"): vol.Schema({ + vol.Required("piles"): vol.All( + cv.ensure_list, + [STOCKPILE_SCHEMA] + ) + }) +}, extra=vol.ALLOW_EXTRA) -from .const import DOMAIN +PLATFORMS = [Platform.SENSOR, Platform.CALENDAR] -PLATFORMS = [Platform.NUMBER] +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the StockPile integration.""" + if "stockpile" not in config: + return True -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Set up StockPile from a config entry.""" - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][entry.entry_id] = entry.data + # Store the configuration in hass.data + hass.data["stockpile"] = {} + conf = config["stockpile"] - # Add reload service - await async_setup_reload_service(hass, DOMAIN, PLATFORMS) + # Set up each stockpile + for pile in conf["piles"]: + pile_name = pile["name"] + hass.data["stockpile"][pile_name] = pile - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - return True + # Forward the setup to the sensor and calendar platforms + for platform in PLATFORMS: + hass.async_create_task( + hass.helpers.discovery.async_load_platform( + platform, "stockpile", conf, config + ) + ) -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Unload a config entry.""" - unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - if unload_ok: - hass.data[DOMAIN].pop(entry.entry_id) - return unload_ok \ No newline at end of file + return True \ No newline at end of file diff --git a/custom_components/stockpile/config_flow.py b/custom_components/stockpile/config_flow.py deleted file mode 100644 index c8eb066..0000000 --- a/custom_components/stockpile/config_flow.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Config flow for StockPile.""" -import voluptuous as vol -from homeassistant import config_entries -from .const import DOMAIN - -class StockPileConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): - """Handle a config flow for StockPile.""" - - VERSION = 1 - - async def async_step_user(self, user_input=None): - """Handle the initial step.""" - if user_input is not None: - # Create unique ID from the name - await self.async_set_unique_id(f"stockpile_{user_input['name'].lower()}") - self._abort_if_unique_id_configured() - - return self.async_create_entry( - title=user_input["name"], - data=user_input - ) - - return self.async_show_form( - step_id="user", - data_schema=vol.Schema({ - vol.Required("name"): str, - vol.Required("initial_quantity", default=0): int, - }) - ) \ No newline at end of file diff --git a/custom_components/stockpile/const.py b/custom_components/stockpile/const.py index 3253257..cf83b3c 100644 --- a/custom_components/stockpile/const.py +++ b/custom_components/stockpile/const.py @@ -1,3 +1,13 @@ -"""Constants for StockPile.""" +"""Constants for the StockPile integration.""" DOMAIN = "stockpile" -MANUFACTURER = "StockPile" \ No newline at end of file + +# Entity naming patterns +PILE_SENSOR_NAME = "pile_{}" +STOCK_SENSOR_NAME = "stock_{}" +CALENDAR_NAME = "pile_{}" + +# Attributes +ATTR_UNIT = "unit" +ATTR_MIN_QUANTITY = "min_quantity" +ATTR_MAX_QUANTITY = "max_quantity" +ATTR_STEP_SIZE = "step_size" \ No newline at end of file diff --git a/custom_components/stockpile/manifest.json b/custom_components/stockpile/manifest.json index 23f17b3..c09343f 100644 --- a/custom_components/stockpile/manifest.json +++ b/custom_components/stockpile/manifest.json @@ -1,10 +1,10 @@ { "domain": "stockpile", "name": "StockPile", - "version": "1.0.1", - "documentation": "https://github.com/jack3308/stockpile", + "documentation": "https://github.com/username/ha-stockpile", "dependencies": [], - "codeowners": ["@jack3308"], - "config_flow": true, - "iot_class": "local_push" + "codeowners": [], + "requirements": [], + "version": "0.1.0", + "iot_class": "calculated" } \ No newline at end of file diff --git a/custom_components/stockpile/number.py b/custom_components/stockpile/number.py deleted file mode 100644 index c0a44f0..0000000 --- a/custom_components/stockpile/number.py +++ /dev/null @@ -1,51 +0,0 @@ -"""StockPile number platform.""" -from homeassistant.components.number import NumberEntity -from homeassistant.components.number import RestoreNumber -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.device_registry import DeviceInfo - - - -from .const import DOMAIN, MANUFACTURER - -async def async_setup_entry( - hass: HomeAssistant, - entry: ConfigEntry, - async_add_entities: AddEntitiesCallback -) -> None: - """Set up number platform.""" - async_add_entities([StockPileNumber(entry)]) - -class StockPileNumber(RestoreNumber, NumberEntity): - """StockPile number class.""" - - def __init__(self, entry: ConfigEntry) -> None: - """Initialize the number.""" - self.entry = entry - self._attr_unique_id = f"stockpile_{entry.data['name'].lower()}" - self._attr_name = entry.data['name'] - self._attr_icon = "mdi:counter" - self._attr_mode = "box" - try: - self._attr_native_value = float(entry.data['initial_quantity']) - except (ValueError, TypeError): - self._attr_native_value = 0.0 - self._attr_native_min_value = 0 - self._attr_native_max_value = 999 - self._attr_native_step = 1 - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, self._attr_unique_id)}, - name=entry.data['name'], - manufacturer=MANUFACTURER - ) - - async def async_added_to_hass(self) -> None: - """Run when entity about to be added to hass.""" - if state := await self.async_get_last_number_data(): - self._attr_native_value = float(state.state) - - async def async_set_native_value(self, value: float) -> None: - """Update the current value.""" - self._attr_native_value = value \ No newline at end of file diff --git a/custom_components/stockpile/sensor.py b/custom_components/stockpile/sensor.py new file mode 100644 index 0000000..64579ee --- /dev/null +++ b/custom_components/stockpile/sensor.py @@ -0,0 +1,130 @@ +"""Sensor platform for StockPile integration.""" +from __future__ import annotations + +import logging +from typing import Any + +from homeassistant.components.sensor import SensorEntity, SensorDeviceClass +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_NAME +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType + +from .const import ( + DOMAIN, + PILE_SENSOR_NAME, + STOCK_SENSOR_NAME, + ATTR_UNIT, + ATTR_MIN_QUANTITY, + ATTR_MAX_QUANTITY, + ATTR_STEP_SIZE, +) + +_LOGGER = logging.getLogger(__name__) + +async def async_setup_platform( + hass: HomeAssistant, + config: ConfigType, + add_entities: AddEntitiesCallback, + discovery_info: DiscoveryInfoType | None = None, +) -> None: + """Set up the StockPile sensor platform.""" + if discovery_info is None: + return + + entities = [] + + # Create sensors for each pile in the configuration + for pile in discovery_info["piles"]: + pile_name = pile["name"] + + # Create device registry entry for this stockpile + device_id = f"stockpile_{pile_name}" + + # Create pile sensor (tracks overall quantity) + entities.append( + PileSensor( + pile_name, + pile["initial_quantity"], + pile["unit"], + device_id, + pile.get("min_quantity", 0), + pile.get("max_quantity"), + pile.get("step_size", 1), + ) + ) + + # Create stock sensor (tracks individual items) + entities.append( + StockSensor( + pile_name, + pile["initial_quantity"], + pile["unit"], + device_id, + ) + ) + + add_entities(entities) + +class PileSensor(SensorEntity): + """Sensor for tracking overall pile quantities.""" + + def __init__( + self, + name: str, + initial_quantity: float, + unit: str, + device_id: str, + min_quantity: float, + max_quantity: float | None, + step_size: float, + ) -> None: + """Initialize the pile sensor.""" + self._attr_name = PILE_SENSOR_NAME.format(name) + self._attr_unique_id = f"pile_{name}" + self._attr_native_value = initial_quantity + self._attr_native_unit_of_measurement = unit + self._attr_device_class = SensorDeviceClass.QUANTITY + + # Device registry info + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, device_id)}, + name=f"StockPile {name}", + manufacturer="StockPile", + model="Pile Tracker", + ) + + # Additional attributes + self._attr_extra_state_attributes = { + ATTR_MIN_QUANTITY: min_quantity, + ATTR_MAX_QUANTITY: max_quantity, + ATTR_STEP_SIZE: step_size, + ATTR_UNIT: unit, + } + +class StockSensor(SensorEntity): + """Sensor for tracking individual stock items.""" + + def __init__( + self, + name: str, + initial_quantity: float, + unit: str, + device_id: str, + ) -> None: + """Initialize the stock sensor.""" + self._attr_name = STOCK_SENSOR_NAME.format(name) + self._attr_unique_id = f"stock_{name}" + self._attr_native_value = initial_quantity + self._attr_native_unit_of_measurement = unit + self._attr_device_class = SensorDeviceClass.QUANTITY + + # Device registry info + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, device_id)}, + name=f"StockPile {name}", + manufacturer="StockPile", + model="Pile Tracker", + ) \ No newline at end of file diff --git a/custom_components/stockpile/services.yaml b/custom_components/stockpile/services.yaml deleted file mode 100644 index 3120cfe..0000000 --- a/custom_components/stockpile/services.yaml +++ /dev/null @@ -1,3 +0,0 @@ -reload: - name: Reload - description: Reload all stockpile entities. \ No newline at end of file diff --git a/dev_plan.md b/dev_plan.md new file mode 100644 index 0000000..23f33d6 --- /dev/null +++ b/dev_plan.md @@ -0,0 +1,201 @@ +# Home Assistant Stockpile Integration + +## 1. Core Concepts & Context Setting + +First, ensure the LLM understands that this is a Home Assistant integration for managing stockpiles of consumable items. The integration needs to track: + +* Quantity of items + +* Consumption patterns + +* Restocking events + +* Scheduling of events + +* Different types of consumables (medication, food, etc.) + +### Key Abstractions + +Have the LLM understand these core concepts: + +* **Pile**: The collective consumable (e.g., "wine", "dog food") + +* **Stock**: The unit of measurement (e.g., "bottle", "meal") + +* **Stockpile**: The device + +## 2. Development Structure + +Guide the LLM to develop the integration in this order: + +### Phase 1: Entity Setup + +1. Create the base number entity for quantity tracking + +2. Implement the calendar entity for event management + +3. Set up the sensor entities for pile and stock information + +### Phase 2: Attributes Implementation + +1. Start with stockpile\_quantity attributes + + * Focus on consumption parameters + + * Implement validation for min/max/step values + +2. Add calendar entity attributes + + * Handle datetime validations + + * Implement cycle management + +3. Implement pile and stock attributes + + * Handle different stock types + + * Implement scheduling logic + +### Phase 3: Actions Development + +Break down each action into smaller components: + +1. `stockpile.consume`: + + * Quantity validation + + * Calendar event creation + + * Schedule management + +2. `stockpile.restock`: + + * Standard vs custom quantity handling + + * Event history management + +3. `stockpile.cycle`: + + * Cycle period management + + * Event chain handling + +4. `stockpile.schedule`: + + * Event creation + + * Data structure validation + +## 3. Critical Implementation Details + +Guide the LLM to pay special attention to: + +### Data Validation + +* Ensure consumption steps are valid multiples + +* Validate date/time inputs + +* Check quantity bounds + +### Event Management + +1. Calendar Events: + + * Always create historical records + + * Store event data in description field + + * Handle event overlaps + +### Scheduling Logic + +1. For periodic consumption: + + * Remove future events appropriately + + * Calculate next event timing + +2. For scheduled consumption: + + * Handle multiple schedule types + + * Manage schedule conflicts + +## 4. Testing Scenarios + +Have the LLM implement tests for: + +1. Basic Operations: + + * Standard consumption + + * Standard restocking + + * Cycle management + +2. Edge Cases: + + * Multiple target handling + + * Schedule conflicts + + * Cycle transitions + +3. Data Validation: + + * Invalid quantities + + * Invalid schedules + + * Invalid cycle periods + +## 5. Implementation Notes + +### Code Organization + +Guide the LLM to: + +1. Separate entity logic + +2. Create utility functions for common operations + +3. Implement proper error handling + +4. Use type hints and documentation + +### Configuration Flow + +1. Device setup + +2. Entity registration + +3. Service registration + +4. Attribute management + +### Best Practices + +1. Follow Home Assistant coding standards + +2. Implement proper state management + +3. Use appropriate debugging + +4. Handle configuration validation + +## 6. Development Workflow + +Guide the LLM through: + +1. Initial setup and configuration validation + +2. Entity implementation + +3. Service implementation + +4. Integration testing + +5. Documentation generation + +Remember to have the LLM validate each step before moving to the next phase. diff --git a/hacs.json b/hacs.json deleted file mode 100644 index 006a40e..0000000 --- a/hacs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "StockPile", - "domain": "stockpile", - "render_readme": true, - "homeassistant": "2024.1.0", - "hacs": "1.0.1", - "version": "2024.12.01" -} \ No newline at end of file diff --git a/instruction_set.md b/instruction_set.md new file mode 100644 index 0000000..4d49db5 --- /dev/null +++ b/instruction_set.md @@ -0,0 +1,202 @@ +### Detailed Instruction Set for LLM + +#### Phase 1: Entity Setup + +1. Implement a device that will be the parent of all entities for each 'stockpile' entry in home assistant + +2. Implement a Sensor Entity for Quantity Tracking + +* Define a new entity class that inherits from Entity. + +* Implement the necessary methods to track the quantity of consumables. + +* Use type annotations for all methods and include docstrings for documentation. + +2. Implement the Calendar Entity for Event Management + +* Create a calendar entity that can manage events related to stockpile consumption and restocking. + +* Ensure that the calendar entity can handle datetime validations. + +* Ensure state restoration using the standard home assistant state persistence + +3. Set Up the Sensor Entities for Pile and Stock Information + +* Create sensor entities to represent different piles and stock types. + +* Implement methods to retrieve and update the state of these sensors. + +##### Notes + +* Each entity should persist across home assistant restarts. + +* Each entity should use the device/stockpile name as a suffix for the entity, ensuring standard formatting and readability + +#### Phase 2: Attributes Implementation + +1. Implement Stockpile Quantity Attributes + +* Add attributes to the stockpile entity to track consumption parameters. + +* Implement validation for minimum, maximum, and step values for the quantity. + +2. Add Calendar Entity Attributes + +* Implement attributes for the calendar entity to manage event cycles and handle datetime validations. + +3. Implement Pile and Stock Attributes + +* Create attributes for different stock types and implement scheduling logic for consumption. + +#### Phase 3: Actions Development + +* Develop the stockpile.consume Action + + + +* Implement quantity validation logic. + + + +* Create calendar events upon consumption. + + + +* Manage scheduling for future consumption events. + +2. Develop the stockpile.restock Action + +* Handle both standard and custom quantity restocking. + + + +* Implement event history management to track restocking events. + + + +* Develop the stockpile.cycle Action + + + +* Manage cycle periods for consumption. + + + +* Implement logic to handle event chains related to cycles. + +4. Develop the stockpile.schedule Action + +* Create events based on user-defined schedules. + + + +* Validate the data structure for scheduling. + +#### Phase 4: Critical Implementation Details + +* Data Validation + + + +* Ensure that all consumption steps are valid multiples. + + + +* Validate date/time inputs and check quantity bounds. + + + +* Event Management + + + +* Create historical records for calendar events. + + + +* Store event data in the description field and handle overlaps. + +3. Scheduling Logic + +* Implement logic for periodic and scheduled consumption, including conflict management. + +#### Phase 5: Testing Scenarios + +1. Basic Operations Testing + +* Write unit tests for standard consumption, restocking, and cycle management. + + + +* Edge Cases Testing + + + +* Implement tests for handling multiple targets, schedule conflicts, and cycle transitions. + +3. Data Validation Testing + +* Create tests for invalid quantities, schedules, and cycle periods. + +#### Phase 6: Implementation Notes + +* Code Organization + + + +* Separate entity logic into different modules (e.g., sensor.py, binary\_sensor.py). + + + +* Create utility functions for common operations and implement error handling. + + + +* Configuration Flow + + + +* Define the flow for device setup, entity registration, service registration, and attribute management. + +3. Best Practices + +* Follow Home Assistant coding standards, implement state management, and handle configuration validation. + +#### Phase 7: Development Workflow + +1. Initial Setup and Configuration Validation + +* Ensure that the integration is properly set up and validated before proceeding. + + + +* Entity Implementation + + + +* Implement the entities as defined in the previous phases. + + + +* Service Implementation + + + +* Define and implement the services that will interact with the entities. + + + +* Integration Testing + + + +* Conduct thorough testing of the integration to ensure functionality. + + + +* Documentation Generation + + + +* Generate documentation for the integration, including usage examples and configuration details. + diff --git a/sample_config b/sample_config new file mode 100644 index 0000000..0af39dd --- /dev/null +++ b/sample_config @@ -0,0 +1,8 @@ +stockpile: + piles: + - name: wine + unit: bottles + initial_quantity: 12 + min_quantity: 2 + max_quantity: 24 + step_size: 1 \ No newline at end of file