Skip to content

Commit

Permalink
first version
Browse files Browse the repository at this point in the history
  • Loading branch information
pantherale0 committed Apr 8, 2024
1 parent 2d39f4a commit ef736e3
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 113 deletions.
110 changes: 62 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,62 @@
# Notice

The component and platforms in this repository are not meant to be used by a
user, but as a "blueprint" that custom component developers can build
upon, to make more awesome stuff.

HAVE FUN! 😎

## Why?

This is simple, by having custom_components look (README + structure) the same
it is easier for developers to help each other and for users to start using them.

If you are a developer and you want to add things to this "blueprint" that you think more
developers will have use for, please open a PR to add it :)

## What?

This repository contains multiple files, here is a overview:

| File | Purpose |  Documentation |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `.devcontainer.json` | Used for development/testing with Visual Studio Code. | [Documentation](https://code.visualstudio.com/docs/remote/containers) |
| `.github/ISSUE_TEMPLATE/*.yml` | Templates for the issue tracker | [Documentation](https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository) |
| `.vscode/tasks.json` | Tasks for the devcontainer. | [Documentation](https://code.visualstudio.com/docs/editor/tasks) |
| `custom_components/integration_blueprint/*` | Integration files, this is where everything happens. | [Documentation](https://developers.home-assistant.io/docs/creating_component_index) |
| `CONTRIBUTING.md` | Guidelines on how to contribute. | [Documentation](https://help.github.com/en/github/building-a-strong-community/setting-guidelines-for-repository-contributors) |
| `LICENSE` | The license file for the project. | [Documentation](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/licensing-a-repository) |
| `README.md` | The file you are reading now, should contain info about the integration, installation and configuration instructions. | [Documentation](https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax) |
| `requirements.txt` | Python packages used for development/lint/testing this integration. | [Documentation](https://pip.pypa.io/en/stable/user_guide/#requirements-files) |

## How?

1. Create a new repository in GitHub, using this repository as a template by clicking the "Use this template" button in the GitHub UI.
1. Open your new repository in Visual Studio Code devcontainer (Preferably with the "`Dev Containers: Clone Repository in Named Container Volume...`" option).
1. Rename all instances of the `integration_blueprint` to `custom_components/<your_integration_domain>` (e.g. `custom_components/awesome_integration`).
1. Rename all instances of the `Anglian Water` to `<Your Integration Name>` (e.g. `Awesome Integration`).
1. Run the `scripts/develop` to start HA and test out your new integration.

## Next steps

These are some next steps you may want to look into:

- Add tests to your integration, [`pytest-homeassistant-custom-component`](https://github.com/MatthewFlamm/pytest-homeassistant-custom-component) can help you get started.
- Add brand images (logo/icon) to https://github.com/home-assistant/brands.
- Create your first release.
- Share your integration on the [Home Assistant Forum](https://community.home-assistant.io/).
- Submit your integration to the [HACS](https://hacs.xyz/docs/publish/start).
# Anglian Water

[![GitHub Release][releases-shield]][releases]
[![GitHub Activity][commits-shield]][commits]
[![License][license-shield]](LICENSE)

![Project Maintenance][maintenance-shield]
[![BuyMeCoffee][buymecoffeebadge]][buymecoffee]

[![Discord][discord-shield]][discord]
[![Community Forum][forum-shield]][forum]

_Integration to integrate with [haanglianwater][haanglianwater]._

**This integration will set up the following platforms.**

| Platform | Description |
| -------- | ---------------------------------------------- |
| `sensor` | Show the previous day water usage information. |

This integration will also collect the past year worth of smart meter readings from your dashboard and import them into a statistic for use with your energy dashboard.

## Installation

1. Using the tool of choice open the directory (folder) for your HA configuration (where you find `configuration.yaml`).
1. If you do not have a `custom_components` directory (folder) there, you need to create it.
1. In the `custom_components` directory (folder) create a new folder called `anglian_water`.
1. Download _all_ the files from the `custom_components/anglian_water/` directory (folder) in this repository.
1. Place the files you downloaded in the new directory (folder) you created.
1. Restart Home Assistant
1. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Anglian Water"

## Configuration is done in the UI

<!---->

You should generally leave the Device ID field blank, the integration will generate this automatically after clicking submit.

Note, during first sign in, the integration needs to "register" Home Assistant as a mobile device to your Anglian Water account, to configure the access token and device IDs correctly a number of requests must be sent in a specific order. Enabling debug mode you will see it makes two requests to "register_device", a request to "get_dashboard_details" and finally a request to "get_bills_payments".

If the integration does not send the above queries in that order, the API to retrieve usage details continues to stay locked and this integration will not work. The integration does not store or process the data returned from the APIs for these extra endpoints, they are simply used to replicate the calls the mobile app creates.

## Contributions are welcome!

If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md)

---

[haanglianwater]: https://github.com/pantherale0/haanglianwater
[buymecoffee]: https://www.buymeacoffee.com/pantherale0
[buymecoffeebadge]: https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?style=for-the-badge
[commits-shield]: https://img.shields.io/github/commit-activity/y/pantherale0/haanglianwater.svg?style=for-the-badge
[commits]: https://github.com/pantherale0/haanglianwater/commits/main
[discord]: https://discord.gg/Qa5fW2R
[discord-shield]: https://img.shields.io/discord/330944238910963714.svg?style=for-the-badge
[exampleimg]: example.png
[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge
[forum]: https://community.home-assistant.io/
[license-shield]: https://img.shields.io/github/license/pantherale0/haanglianwater.svg?style=for-the-badge
[maintenance-shield]: https://img.shields.io/badge/maintainer-Joakim%20Sørensen%20%40pantherale0-blue.svg?style=for-the-badge
[releases-shield]: https://img.shields.io/github/release/pantherale0/haanglianwater.svg?style=for-the-badge
[releases]: https://github.com/pantherale0/haanglianwater/releases
54 changes: 0 additions & 54 deletions README_EXAMPLE.md

This file was deleted.

5 changes: 3 additions & 2 deletions custom_components/anglian_water/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ async def async_step_user(
if user_input is not None:
try:
if user_input.get(CONF_DEVICE_ID, "") == "":
await API.create_via_login(
auth = await API.create_via_login(
email=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
)
else:
await API.create_via_login_existing_device(
auth = await API.create_via_login_existing_device(
email=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
dev_id=user_input[CONF_DEVICE_ID],
Expand All @@ -47,6 +47,7 @@ async def async_step_user(
LOGGER.exception(exception)
_errors["base"] = "unknown"
else:
user_input[CONF_DEVICE_ID] = auth.device_id
return self.async_create_entry(
title=user_input[CONF_USERNAME],
data=user_input,
Expand Down
83 changes: 79 additions & 4 deletions custom_components/anglian_water/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
"""DataUpdateCoordinator for integration_blueprint."""

from __future__ import annotations
import time
from datetime import timedelta, date, datetime

from datetime import timedelta

from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
from homeassistant.components.recorder.statistics import (
async_add_external_statistics,
get_last_statistics,
statistic_during_period,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator,
UpdateFailed,
)
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.util import dt as dt_util
from pyanglianwater import AnglianWater
from pyanglianwater.exceptions import (
InvalidPasswordError,
Expand All @@ -21,6 +29,10 @@
from .const import DOMAIN, LOGGER


def is_dst(utc_dt: datetime):
return bool(time.localtime(utc_dt.timestamp()).tm_isdst)


# https://developers.home-assistant.io/docs/integration_fetching_data#coordinated-single-api-poll-for-data-for-all-entities
class AnglianWaterDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the API."""
Expand All @@ -38,16 +50,79 @@ def __init__(
hass=hass,
logger=LOGGER,
name=DOMAIN,
update_interval=timedelta(minutes=120),
update_interval=timedelta(minutes=20),
)

async def _async_update_data(self):
"""Update data via library."""
try:
return await self.client.update()
await self.client.update()
await self._insert_statistics()
except InvalidUsernameError as exception:
raise ConfigEntryAuthFailed(exception) from exception
except InvalidPasswordError as exception:
raise ConfigEntryAuthFailed(exception) from exception
except UnknownEndpointError as exception:
raise UpdateFailed(exception) from exception

async def _insert_statistics(self):
"""Insert Anglian Water stats."""
stat_id = f"{DOMAIN}:anglian_water_previous_consumption"
try:
last_stats = await get_instance(self.hass).async_add_executor_job(
get_last_statistics, self.hass, 1, stat_id, True, "sum"
)
except AttributeError:
last_stats = None
if not last_stats:
# First time lets insert last year of data
hourly_consumption_data = await self.client.get_usages(
start=date.today() - timedelta(days=365),
end=date.today(),
)
last_stats_time = None
else:
# We will just use the most recent data
hourly_consumption_data = await self.client.get_usages(
start=date.today() - timedelta(days=1), end=date.today()
)
start = dt_util.parse_datetime(
hourly_consumption_data[0]["meterReadTimestamp"]
)
stat = await get_instance(self.hass).async_add_executor_job(
statistic_during_period,
self.hass,
start,
None,
[stat_id],
"hour",
True,
)
last_stats_time = stat[stat_id][0]["start"]

statistics = []
for reading in hourly_consumption_data["readings"]:
start = dt_util.parse_datetime(reading["meterReadTimestamp"] + "+00:00")
if is_dst(start):
start = dt_util.parse_datetime(reading["meterReadTimestamp"] + "+01:00")
if last_stats_time is not None and start <= last_stats_time:
continue
# remove an hour from the start time data rec for hour is actually for the last hour
# eg received at 10am is for 9-10am and will show incorrectly in HASS energy dashboard
statistics.append(
StatisticData(
start=start - timedelta(hours=1),
state=reading["consumption"],
sum=int(reading["meterReadValue"]) / 1000,
)
)

metadata = StatisticMetaData(
has_mean=False,
has_sum=True,
name="Previous Water Consumption",
source=DOMAIN,
statistic_id=stat_id,
unit_of_measurement="m³",
)
async_add_external_statistics(self.hass, metadata, statistics)
2 changes: 1 addition & 1 deletion custom_components/anglian_water/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"anglian_water"
],
"requirements": [
"pyanglianwater==0.1.0"
"pyanglianwater==0.1.1"
],
"version": "0.0.0"
}
4 changes: 1 addition & 3 deletions custom_components/anglian_water/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
SensorEntity,
SensorEntityDescription,
SensorDeviceClass,
SensorStateClass,
)

from .const import DOMAIN
Expand All @@ -18,9 +17,8 @@
key="anglian_water_previous_consumption",
name="Previous Consumption",
icon="mdi:water",
native_unit_of_measurement="m3",
native_unit_of_measurement="",
device_class=SensorDeviceClass.WATER,
state_class=SensorStateClass.TOTAL,
),
)

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ colorlog==6.7.0
homeassistant==2023.8.0
pip>=21.0,<23.2
ruff==0.0.292
pyanglianwater==0.1.0
pyanglianwater==0.1.1

0 comments on commit ef736e3

Please sign in to comment.