Skip to content

Commit

Permalink
V1.0.0 (#282)
Browse files Browse the repository at this point in the history
* Align with Home Assistant style guidelines

* Update dependencies

* Switch VS Code format provider

* Update manifest

* Refactor config flow and init

* Remove tests

* Handle offline setup failure

* Add comments

* Use latest code for pyglowmarkt

* Rewrite the whole of sensor.py

* Update hacs.json and bump minimum hass version

* Format code with black and isort

* Remove blank line

* Update readme

* Add note about changeover

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
  • Loading branch information
HandyHat and deepsource-autofix[bot] authored Jan 1, 2023
1 parent 2565cf7 commit e9d4219
Show file tree
Hide file tree
Showing 21 changed files with 584 additions and 1,030 deletions.
6 changes: 5 additions & 1 deletion .deepsource.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ enabled = true
runtime_version = "3.x.x"

[[transformers]]
name = "autopep8"
name = "black"
enabled = true

[[transformers]]
name = "isort"
enabled = true
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.provider": "autopep8",
"python.formatting.provider": "black",
"files.associations": {
"*.yaml": "home-assistant"
}
Expand Down
61 changes: 38 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,61 @@

Home Assistant integration for energy consumption data from UK SMETS (Smart) meters using the Hildebrand Glow API.

This integration works without requiring a consumer device provided by Hildebrand Glow and can work with your existing smart meter. You'll need to set up your smart meter for free in the Bright app on [Android](https://play.google.com/store/apps/details?id=uk.co.hildebrand.brightionic&hl=en_GB) or [iOS](https://apps.apple.com/gb/app/bright/id1369989022). This will only work when using the Data Communications Company (DCC) backend, which all SMETS 2 meters and some SMETS 1 meters do ([more information](https://www.smartme.co.uk/technical.html)). Once you can see your data in the app, you are good to go.
This integration works without requiring a consumer device provided by Hildebrand Glow and can work with your existing smart meter. You'll need to set up your smart meter for free in the Bright app on [Android](https://play.google.com/store/apps/details?id=uk.co.hildebrand.brightionic&hl=en_GB) or [iOS](https://apps.apple.com/gb/app/bright/id1369989022). This will only work when using the Data Communications Company (DCC) backend, which all [SMETS 2 meters](https://www.smartme.co.uk/smets-2.html) and some [SMETS 1 meters](https://www.smartme.co.uk/smets-1.html) do. Once you can see your data in the app, you are good to go.

The data provided will be delayed by around 30 minutes. Unfortunately this means the daily sensors lose the last 30 mins of the day. To get real-time consumption data, you can buy [Hildebrand Glow hardware](https://shop.glowmarkt.com/). Although this integration will work with their hardware, you should use the MQTT version [here](https://github.com/unlobito/ha-hildebrandglow/) to get real-time consumption data.
The data provided will be delayed by around 30 minutes. To get real-time consumption data, you can buy [Hildebrand Glow hardware](https://shop.glowmarkt.com/). Although this integration will technically work with their hardware, you should instead use the MQTT version [here](https://github.com/unlobito/ha-hildebrandglow/) to get real-time consumption data.

## Installation

### Automated installation through HACS

You can install this component through [HACS](https://hacs.xyz/) to easily receive updates.
You can install this component through [HACS](https://hacs.xyz/) to easily receive updates. Once HACS is installed, click this link:

After installing HACS, visit the HACS _Integrations_ pane and add `https://github.com/HandyHat/ha-hildebrandglow-dcc` as an `Integration` by following [these instructions](https://hacs.xyz/docs/faq/custom_repositories/). You'll then be able to install it through the _Integrations_ pane.
[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=HandyHat&repository=ha-hildebrandglow-dcc)

<details>
<summary>Manually add to HACS</summary>
Visit the HACS Integrations pane and add `https://github.com/HandyHat/ha-hildebrandglow-dcc` as an `Integration` by following [these instructions](https://hacs.xyz/docs/faq/custom_repositories/). You'll then be able to install it through the Integrations pane.
</details>

### Manual installation

Copy the `custom_components/hildebrandglow_dcc/` directory and all of its files to your `config/custom_components` directory. You'll then need to restart Home Assistant for it to detect the new integration.
Copy the `custom_components/hildebrandglow_dcc/` directory and all of its files to your `config/custom_components/` directory.

## Configuration

Visit the _Integrations_ section within Home Assistant's _Configuration_ panel and click the _Add_ button in the bottom right corner. After searching for "Hildebrand Glow", you'll be asked for your Glow credentials.
Once installed, restart Home Assistant:

[![Open your Home Assistant instance and show the system dashboard.](https://my.home-assistant.io/badges/system_dashboard.svg)](https://my.home-assistant.io/redirect/system_dashboard/)

Then, add the integration:

[![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=hildebrandglow_dcc)

Once you've authenticated to Glow, the integration will automatically set up the following sensors for each of the smart meters on your account:

### Electricity Sensors
<details>
<summary>Manually add the Integration</summary>
Visit the Integrations section in Home Assistant and click the add button in the bottom right corner. Search for Hildebrand Glow (DCC) and input your credentials. <b>You may need to clear your browser cache before the integration appears in the list.</b>
</details>

- Electric Consumption (Today) - Consumption today (kWh)
- Electric Consumption (Year) - Consumption for the year to date (kWh)
- Electric Cost (Today) - Cost of electricity used today (GBP)
- Electric Tariff Standing - Today's standing charge for electricity (GBP)
- Electric Tariff Rate - Current tariff (GBP/kWh)
## Sensors

### Gas Sensors
Once you've authenticated, the integration will automatically set up the following sensors for each of the smart meters on your account:

- Gas Consumption (Today) - Consumption today (kWh)
- Gas Consumption (Year) - Consumption for the year to date (kWh)
- Gas Cost (Today) - Cost of gas used today (GBP)
- Gas Tariff Standing - Today's standing charge for gas (GBP)
- Gas Tariff Rate - Current tariff (GBP/kWh)
- Usage (Today) - Consumption today (kWh)
- Cost (Today) - Cost of electricity used today (GBP)
- Standing Charge - Today's standing charge for electricity (GBP)
- Rate - Current tariff (GBP/kWh)

The usage and cost sensors will still show the previous day's data until shortly after 00:30 to ensure that all of the previous day's data is collected.
The standing charge and rate sensors are disabled by default as they are less commonly used. Before enabling them, ensure the data is visible in the Bright app.

## Energy Management

The sensors created integrate directly into Home Assistant's [Home Energy Management](https://www.home-assistant.io/docs/energy/).
It is recommended you use the yearly sensors in the Energy integration.
It is recommended you use the daily usage and cost sensors in the Energy integration.

[![Open your Home Assistant instance and show your Energy configuration panel.](https://my.home-assistant.io/badges/config_energy.svg)](https://my.home-assistant.io/redirect/config_energy/)

## Debugging

Expand All @@ -73,20 +86,22 @@ python -m venv dev-venv
You can then install the dependencies that will allow you to develop:
`pip3 install -r requirements-dev.txt`

This will install `homeassistant`, `autopep8`, `isort` and `pylint`.
This will install `black`, `homeassistant`, `isort`, `pyglowmarkt` and `pylint`.

### Code Style

This project makes use of isort, pylint and autopep8 to enforce a consistent code style across the codebase.
This project makes use of black, isort and pylint to enforce a consistent code style across the codebase.

## Credits

Thanks go to:

- The [original project](https://github.com/unlobito/ha-hildebrandglow) from which this project is forked. It currently provides MQTT access for those with Hildebrand CAD Hardware.
- The [pyglowmarkt](https://github.com/cybermaggedon/pyglowmarkt) library, which is used to interact with the Hildebrand API.

- The Hildebrand API [documentation](https://docs.glowmarkt.com/GlowmarktAPIDataRetrievalDocumentationIndividualUserForBright.pdf) and [Swagger UI](https://api.beething.com/api-docs/v0-1/resourcesys/).

- The [original project](https://github.com/unlobito/ha-hildebrandglow) from which this project is forked.

- The [Hildebrand-Glow-Python-Library](https://github.com/ghostseven/Hildebrand-Glow-Python-Library), used for understanding the API.

- All of the contributors and users, without whom this integration wouldn't be where it is today.
64 changes: 32 additions & 32 deletions custom_components/hildebrandglow_dcc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
"""The Hildebrand Glow integration."""
import asyncio
from typing import Any, Dict
"""The Hildebrand Glow (DCC) integration."""
from __future__ import annotations

import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .const import APP_ID, DOMAIN
from .glow import Glow
import logging

CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)
from glowmarkt import BrightClient
import requests

PLATFORMS = ["sensor"]
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

from .const import DOMAIN

async def async_setup(hass: HomeAssistant, _unused: Dict[str, Any]) -> bool:
"""Set up the Hildebrand Glow component."""
hass.data[DOMAIN] = {}
_LOGGER = logging.getLogger(__name__)

return True
PLATFORMS: list[Platform] = [Platform.SENSOR]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Hildebrand Glow from a config entry."""
glow = Glow(APP_ID, entry.data["token"])
hass.data[DOMAIN][entry.entry_id] = glow

for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
"""Set up Hildebrand Glow (DCC) from a config entry."""
hass.data.setdefault(DOMAIN, {})
# Authenticate with the API
try:
glowmarkt = await hass.async_add_executor_job(
BrightClient, entry.data["username"], entry.data["password"]
)
except requests.Timeout as ex:
raise ConfigEntryNotReady(f"Timeout: {ex}") from ex
except requests.exceptions.ConnectionError as ex:
raise ConfigEntryNotReady(f"Cannot connect: {ex}") from ex
except Exception as ex: # pylint: disable=broad-except
raise ConfigEntryNotReady(f"Unexpected exception: {ex}") from ex
else:
_LOGGER.debug("Successful Post to %sauth", glowmarkt.url)

# Set API object
hass.data[DOMAIN][entry.entry_id] = glowmarkt

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""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:
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
92 changes: 51 additions & 41 deletions custom_components/hildebrandglow_dcc/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,83 @@
"""Config flow for Hildebrand Glow integration."""
"""Config flow for Hildebrand Glow (DCC) integration."""
from __future__ import annotations

import logging
from typing import Any, Dict
from typing import Any

from glowmarkt import BrightClient
import requests
import voluptuous as vol
from homeassistant import config_entries, core, data_entry_flow

from .const import APP_ID, DOMAIN
from .glow import CannotConnect, Glow, InvalidAuth
from homeassistant import config_entries
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

DATA_SCHEMA = vol.Schema(
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required("username"): str,
vol.Required("password"): str,
}
)


def config_object(data: dict, glow: Dict[str, Any]) -> Dict[str, Any]:
"""Prepare a ConfigEntity with authentication data and a temporary token."""
return {
"name": glow["name"],
"username": data["username"],
"password": data["password"],
"token": glow["token"],
"token_exp": glow["exp"],
}


async def validate_input(hass: core.HomeAssistant, data: dict) -> Dict[str, Any]:
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
"""Validate the user input allows us to connect.
Data has the keys from DATA_SCHEMA with values provided by the user.
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
"""
glow = await hass.async_add_executor_job(
Glow.authenticate, APP_ID, data["username"], data["password"]
glowmarkt = await hass.async_add_executor_job(
BrightClient, data["username"], data["password"]
)
_LOGGER.debug("Successful Post to %sauth", glowmarkt.url)

# Return some info we want to store in the config entry.
return config_object(data, glow)
# Return title of the entry to be added
return {"title": "Hildebrand Glow (DCC)"}


class DomainConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Hildebrand Glow."""
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Hildebrand Glow (DCC)."""

VERSION = 1
CONNECTION_CLASS = config_entries.SOURCE_USER

async def async_step_user(
self, user_input: Dict = None
) -> data_entry_flow.FlowResult:
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
# If left empty, simply show the form again
if user_input is None:
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
)

errors = {}
if user_input is not None:
try:
if self.hass is None:
raise AssertionError
info = await validate_input(self.hass, user_input)

return self.async_create_entry(title=info["name"], data=info)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:

# Test authenticating with the API
try:
info = await validate_input(self.hass, user_input)
except requests.Timeout as ex:
_LOGGER.debug("Timeout: %s", ex)
errors["base"] = "timeout_connect"
except requests.exceptions.ConnectionError as ex:
_LOGGER.debug("Cannot connect: %s", ex)
errors["base"] = "cannot_connect"
# Can't use the RuntimeError exception from the library as it's not a subclass of Exception
except Exception as ex: # pylint: disable=broad-except
if "Authentication failed" in str(ex):
_LOGGER.debug("Authentication Failed")
errors["base"] = "invalid_auth"
elif "Expected an authentication token" in str(ex):
_LOGGER.debug("Expected an authentication token but didn't get one")
errors["base"] = "invalid_auth"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
else:
_LOGGER.exception("Unexpected exception: %s", ex)
errors["base"] = "unknown"
else:
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
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)
3 changes: 1 addition & 2 deletions custom_components/hildebrandglow_dcc/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"""Constants for the Hildebrand Glow integration."""
"""Constants for the Hildebrand Glow (DCC) integration."""

DOMAIN = "hildebrandglow_dcc"
APP_ID = "b0f1b774-a586-4f72-9edd-27ead8aa7a8d"
Loading

0 comments on commit e9d4219

Please sign in to comment.