-
-
Notifications
You must be signed in to change notification settings - Fork 34.9k
New integration featuring the green planet energy prices API for tariff with dynamic prices #150010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
- Add hourly electricity price sensors for business hours (9-18) - Implement JSON-RPC API client with proper error handling - Include comprehensive test suite with >95% coverage - Meet Home Assistant Core Bronze quality standards - Support config flow with unique entry validation - Add runtime data for quality scale compliance
- Removed config flow, coordinator, and sensor files as part of the integration cleanup. - Deleted constants and manifest files to streamline the integration structure. - Updated the sensor implementation to support 24-hour pricing and added special sensors for highest, lowest, and current prices. - Enhanced error handling and response processing in the API communication. - Added installation script for easier setup of the integration. - Updated tests to reflect changes in sensor setup and values, ensuring coverage for new features. - Translated strings to English and improved naming conventions for clarity.
…net Energy integration
…nd improve error handling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍 |
…g unused imports and redundant lines of code.
@joostlek thanks for the review! I applied your change requests. Got a bit bigger due to the changes to a service but I guess it makes more sense with the service. Let me know if there is more. |
return True | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's extend the ConfigEntry type as described in the runtime-data rule
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a greenplanetconfigentry type
if user_input is not None: | ||
return self.async_create_entry(title="Green Planet Energy", data=user_input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's still try to fetch some data. Mostly because the config flow is the best way to tell the user something is wrong. Maybe they have DNS set up incorrectly, or they have a pi-hole running which blocks it. This way we can guarantee that it works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a test
@property | ||
def extra_state_attributes(self) -> dict[str, Any] | None: | ||
"""Return additional state attributes.""" | ||
if self.entity_description.key == "gpe_highest_price_today": | ||
# Find the hour with the highest price | ||
highest_price = get_highest_price_today(self.coordinator.data) | ||
highest_hour = None | ||
if highest_price is not None and self.coordinator.data: | ||
for hour in range(24): | ||
price_key = f"gpe_price_{hour:02d}" | ||
if self.coordinator.data.get(price_key) == highest_price: | ||
highest_hour = hour | ||
break | ||
return { | ||
"highest_price_hour": highest_hour, | ||
"time_slot": f"{highest_hour:02d}:00-{highest_hour + 1:02d}:00" | ||
if highest_hour is not None | ||
else None, | ||
} | ||
|
||
if self.entity_description.key == "gpe_lowest_price_day": | ||
# Find the hour with the lowest price during day (6-18) | ||
lowest_price = get_lowest_price_day(self.coordinator.data) | ||
lowest_hour = None | ||
if lowest_price is not None and self.coordinator.data: | ||
for hour in range(6, 18): | ||
price_key = f"gpe_price_{hour:02d}" | ||
if self.coordinator.data.get(price_key) == lowest_price: | ||
lowest_hour = hour | ||
break | ||
return { | ||
"lowest_price_hour": lowest_hour, | ||
"time_slot": f"{lowest_hour:02d}:00-{lowest_hour + 1:02d}:00" | ||
if lowest_hour is not None | ||
else None, | ||
"period": "day (06:00-18:00)", | ||
} | ||
|
||
if self.entity_description.key == "gpe_lowest_price_night": | ||
# Find the hour with the lowest price during night (18-6) | ||
lowest_price = get_lowest_price_night(self.coordinator.data) | ||
lowest_hour = None | ||
if lowest_price is not None and self.coordinator.data: | ||
# Check evening hours (18-23) | ||
for hour in range(18, 24): | ||
price_key = f"gpe_price_{hour:02d}" | ||
if self.coordinator.data.get(price_key) == lowest_price: | ||
lowest_hour = hour | ||
break | ||
# Check early morning hours (0-5) if not found | ||
if lowest_hour is None: | ||
for hour in range(6): | ||
price_key = f"gpe_price_{hour:02d}" | ||
if self.coordinator.data.get(price_key) == lowest_price: | ||
lowest_hour = hour | ||
break | ||
return { | ||
"lowest_price_hour": lowest_hour, | ||
"time_slot": f"{lowest_hour:02d}:00-{lowest_hour + 1:02d}:00" | ||
if lowest_hour is not None | ||
else None, | ||
"period": "night (18:00-06:00)", | ||
} | ||
|
||
if self.entity_description.key == "gpe_current_price": | ||
current_hour = dt_util.now().hour | ||
return { | ||
"current_hour": current_hour, | ||
"time_slot": f"{current_hour:02d}:00-{current_hour + 1:02d}:00", | ||
} | ||
|
||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove the extra state attributes for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed all but time_slot b/c that one is important for the usecase - otherwise I cannot show to the user when the price is actually cheap.
if not self.coordinator.data: | ||
return None | ||
|
||
# Use value_fn to get calculated values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not self.coordinator.data: | |
return None | |
# Use value_fn to get calculated values |
coordinator data is never None
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, if it could be an empty dict, it should raise UpdateFailed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the "none" case. I guess the updatefailed expection should be raised in the coordinator
if self.entity_description.value_fn: | ||
return self.entity_description.value_fn(self.coordinator.data) | ||
|
||
# Fallback to coordinator data | ||
return self.coordinator.data.get(self.entity_description.key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They all have value_fns
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed the fallback - but still need the return or ?
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
@joostlek I addressed the comments and finally also got the connected branding PR updated with the official files |
Breaking change
Proposed change
🌱 Add Green Planet Energy Integration
This PR introduces a new integration for Green Planet Energy, a German renewable energy provider, offering real-time electricity pricing data to help Home Assistant users optimize their power consumption based on hourly electricity rates.
sensor.gpe_price_00
throughsensor.gpe_price_23
showing electricity prices for each hoursensor.gpe_current_price
- Current hour's electricity pricesensor.gpe_highest_price_today
- Peak price of the daysensor.gpe_lowest_price_day
- Cheapest price during day hours (06:00-18:00)sensor.gpe_lowest_price_night
- Cheapest price during night hours (18:00-06:00)sensor.gpe_price_chart_24h
- 24-hour chart data for visualizationThe integration uses the following dependency: https://github.com/petschni/greenplanet-energy-api
Type of change
Additional information
Checklist
ruff format homeassistant tests
)If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest
.requirements_all.txt
.Updated by running
python3 -m script.gen_requirements_all
.To help with the load of incoming pull requests: