forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add griddy integration (home-assistant#32591)
* Add griddy integration * Griddy is a wholesale power provider in Texas * Supports all four load zones in Texas * Provides real time power price which is useful for automations to handle demand response * Update homeassistant/components/griddy/sensor.py Co-Authored-By: Paulus Schoutsen <[email protected]> * Update homeassistant/components/griddy/config_flow.py Co-Authored-By: Paulus Schoutsen <[email protected]> * Add ability request updated via entity update service. * Improve error message about already configured * Remove DEVICE_CLASS_POWER since we do not have a device class for cost * remove setdefault that was left from previous refactor * More detail on data naming * Bump translation for testing * git add the config flow tests * s/PlatformNotReady/ConfigEntryNotReady/ * Review items * git add the other missing file * Patch griddypower * reduce * adjust target Co-authored-by: Paulus Schoutsen <[email protected]>
- Loading branch information
Showing
15 changed files
with
1,012 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"config" : { | ||
"error" : { | ||
"cannot_connect" : "Failed to connect, please try again", | ||
"unknown" : "Unexpected error" | ||
}, | ||
"title" : "Griddy", | ||
"step" : { | ||
"user" : { | ||
"description" : "Your Load Zone is in your Griddy account under “Account > Meter > Load Zone.”", | ||
"data" : { | ||
"loadzone" : "Load Zone (Settlement Point)" | ||
}, | ||
"title" : "Setup your Griddy Load Zone" | ||
} | ||
}, | ||
"abort" : { | ||
"already_configured" : "This Load Zone is already configured" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
"""The Griddy Power integration.""" | ||
import asyncio | ||
from datetime import timedelta | ||
import logging | ||
|
||
from griddypower.async_api import LOAD_ZONES, AsyncGriddy | ||
import voluptuous as vol | ||
|
||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.exceptions import ConfigEntryNotReady | ||
from homeassistant.helpers import aiohttp_client | ||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator | ||
|
||
from .const import CONF_LOADZONE, DOMAIN, UPDATE_INTERVAL | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
CONFIG_SCHEMA = vol.Schema( | ||
{DOMAIN: vol.Schema({vol.Required(CONF_LOADZONE): vol.In(LOAD_ZONES)})}, | ||
extra=vol.ALLOW_EXTRA, | ||
) | ||
|
||
PLATFORMS = ["sensor"] | ||
|
||
|
||
async def async_setup(hass: HomeAssistant, config: dict): | ||
"""Set up the Griddy Power component.""" | ||
|
||
hass.data.setdefault(DOMAIN, {}) | ||
conf = config.get(DOMAIN) | ||
|
||
if not conf: | ||
return True | ||
|
||
hass.async_create_task( | ||
hass.config_entries.flow.async_init( | ||
DOMAIN, | ||
context={"source": SOURCE_IMPORT}, | ||
data={CONF_LOADZONE: conf.get(CONF_LOADZONE)}, | ||
) | ||
) | ||
return True | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): | ||
"""Set up Griddy Power from a config entry.""" | ||
|
||
entry_data = entry.data | ||
|
||
async_griddy = AsyncGriddy( | ||
aiohttp_client.async_get_clientsession(hass), | ||
settlement_point=entry_data[CONF_LOADZONE], | ||
) | ||
|
||
async def async_update_data(): | ||
"""Fetch data from API endpoint.""" | ||
return await async_griddy.async_getnow() | ||
|
||
coordinator = DataUpdateCoordinator( | ||
hass, | ||
_LOGGER, | ||
name="Griddy getnow", | ||
update_method=async_update_data, | ||
update_interval=timedelta(seconds=UPDATE_INTERVAL), | ||
) | ||
|
||
await coordinator.async_refresh() | ||
|
||
if not coordinator.last_update_success: | ||
raise ConfigEntryNotReady | ||
|
||
hass.data[DOMAIN][entry.entry_id] = coordinator | ||
|
||
for component in PLATFORMS: | ||
hass.async_create_task( | ||
hass.config_entries.async_forward_entry_setup(entry, component) | ||
) | ||
|
||
return True | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): | ||
"""Unload a config entry.""" | ||
unload_ok = all( | ||
await asyncio.gather( | ||
*[ | ||
hass.config_entries.async_forward_entry_unload(entry, component) | ||
for component in PLATFORMS | ||
] | ||
) | ||
) | ||
if unload_ok: | ||
hass.data[DOMAIN].pop(entry.entry_id) | ||
|
||
return unload_ok |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
"""Config flow for Griddy Power integration.""" | ||
import asyncio | ||
import logging | ||
|
||
from aiohttp import ClientError | ||
from griddypower.async_api import LOAD_ZONES, AsyncGriddy | ||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries, core, exceptions | ||
from homeassistant.helpers import aiohttp_client | ||
|
||
from .const import CONF_LOADZONE | ||
from .const import DOMAIN # pylint:disable=unused-import | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
DATA_SCHEMA = vol.Schema({vol.Required(CONF_LOADZONE): vol.In(LOAD_ZONES)}) | ||
|
||
|
||
async def validate_input(hass: core.HomeAssistant, data): | ||
"""Validate the user input allows us to connect. | ||
Data has the keys from DATA_SCHEMA with values provided by the user. | ||
""" | ||
client_session = aiohttp_client.async_get_clientsession(hass) | ||
|
||
try: | ||
await AsyncGriddy( | ||
client_session, settlement_point=data[CONF_LOADZONE] | ||
).async_getnow() | ||
except (asyncio.TimeoutError, ClientError): | ||
raise CannotConnect | ||
|
||
# Return info that you want to store in the config entry. | ||
return {"title": f"Load Zone {data[CONF_LOADZONE]}"} | ||
|
||
|
||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a config flow for Griddy Power.""" | ||
|
||
VERSION = 1 | ||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL | ||
|
||
async def async_step_user(self, user_input=None): | ||
"""Handle the initial step.""" | ||
errors = {} | ||
info = None | ||
if user_input is not None: | ||
try: | ||
info = await validate_input(self.hass, user_input) | ||
except CannotConnect: | ||
errors["base"] = "cannot_connect" | ||
except Exception: # pylint: disable=broad-except | ||
_LOGGER.exception("Unexpected exception") | ||
errors["base"] = "unknown" | ||
|
||
if "base" not in errors: | ||
await self.async_set_unique_id(user_input[CONF_LOADZONE]) | ||
self._abort_if_unique_id_configured() | ||
return self.async_create_entry(title=info["title"], data=user_input) | ||
|
||
return self.async_show_form( | ||
step_id="user", data_schema=DATA_SCHEMA, errors=errors | ||
) | ||
|
||
async def async_step_import(self, user_input): | ||
"""Handle import.""" | ||
await self.async_set_unique_id(user_input[CONF_LOADZONE]) | ||
self._abort_if_unique_id_configured() | ||
|
||
return await self.async_step_user(user_input) | ||
|
||
|
||
class CannotConnect(exceptions.HomeAssistantError): | ||
"""Error to indicate we cannot connect.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
"""Constants for the Griddy Power integration.""" | ||
|
||
DOMAIN = "griddy" | ||
|
||
UPDATE_INTERVAL = 90 | ||
|
||
CONF_LOADZONE = "loadzone" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"domain": "griddy", | ||
"name": "Griddy Power", | ||
"config_flow": true, | ||
"documentation": "https://www.home-assistant.io/integrations/griddy", | ||
"requirements": ["griddypower==0.1.0"], | ||
"ssdp": [], | ||
"zeroconf": [], | ||
"homekit": {}, | ||
"dependencies": [], | ||
"codeowners": [ | ||
"@bdraco" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
"""Support for August sensors.""" | ||
import logging | ||
|
||
from homeassistant.helpers.entity import Entity | ||
|
||
from .const import CONF_LOADZONE, DOMAIN | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
async def async_setup_entry(hass, config_entry, async_add_entities): | ||
"""Set up the August sensors.""" | ||
coordinator = hass.data[DOMAIN][config_entry.entry_id] | ||
|
||
settlement_point = config_entry.data[CONF_LOADZONE] | ||
|
||
async_add_entities([GriddyPriceSensor(settlement_point, coordinator)], True) | ||
|
||
|
||
class GriddyPriceSensor(Entity): | ||
"""Representation of an August sensor.""" | ||
|
||
def __init__(self, settlement_point, coordinator): | ||
"""Initialize the sensor.""" | ||
self._coordinator = coordinator | ||
self._settlement_point = settlement_point | ||
|
||
@property | ||
def unit_of_measurement(self): | ||
"""Return the unit of measurement.""" | ||
return "¢/kWh" | ||
|
||
@property | ||
def name(self): | ||
"""Device Name.""" | ||
return f"{self._settlement_point} Price Now" | ||
|
||
@property | ||
def icon(self): | ||
"""Device Ice.""" | ||
return "mdi:currency-usd" | ||
|
||
@property | ||
def unique_id(self): | ||
"""Device Uniqueid.""" | ||
return f"{self._settlement_point}_price_now" | ||
|
||
@property | ||
def available(self): | ||
"""Return True if entity is available.""" | ||
return self._coordinator.last_update_success | ||
|
||
@property | ||
def state(self): | ||
"""Get the current price.""" | ||
return round(float(self._coordinator.data.now.price_cents_kwh), 4) | ||
|
||
@property | ||
def should_poll(self): | ||
"""Return False, updates are controlled via coordinator.""" | ||
return False | ||
|
||
async def async_update(self): | ||
"""Update the entity. | ||
Only used by the generic entity update service. | ||
""" | ||
await self._coordinator.async_request_refresh() | ||
|
||
async def async_added_to_hass(self): | ||
"""Subscribe to updates.""" | ||
self._coordinator.async_add_listener(self.async_write_ha_state) | ||
|
||
async def async_will_remove_from_hass(self): | ||
"""Undo subscription.""" | ||
self._coordinator.async_remove_listener(self.async_write_ha_state) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"config" : { | ||
"error" : { | ||
"cannot_connect" : "Failed to connect, please try again", | ||
"unknown" : "Unexpected error" | ||
}, | ||
"title" : "Griddy", | ||
"step" : { | ||
"user" : { | ||
"description" : "Your Load Zone is in your Griddy account under “Account > Meter > Load Zone.”", | ||
"data" : { | ||
"loadzone" : "Load Zone (Settlement Point)" | ||
}, | ||
"title" : "Setup your Griddy Load Zone" | ||
} | ||
}, | ||
"abort" : { | ||
"already_configured" : "This Load Zone is already configured" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ | |
"gios", | ||
"glances", | ||
"gpslogger", | ||
"griddy", | ||
"hangouts", | ||
"heos", | ||
"hisense_aehw4a1", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Tests for the Griddy Power integration.""" |
Oops, something went wrong.