Skip to content

Commit 226e9e5

Browse files
authored
Merge pull request #462 from custom-components/service_call
Service call
2 parents 61f121f + 04e3194 commit 226e9e5

File tree

14 files changed

+609
-713
lines changed

14 files changed

+609
-713
lines changed

.devcontainer/devcontainer.json

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
1-
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
21
{
3-
"image": "ghcr.io/ludeeus/devcontainer/integration:stable",
4-
"name": "Nordpool integration development",
5-
"context": "..",
6-
"appPort": [
7-
"9123:8123"
2+
"name": "ludeeus/integration_blueprint",
3+
"image": "mcr.microsoft.com/devcontainers/python:3.12",
4+
"postCreateCommand": "scripts/setup",
5+
"forwardPorts": [
6+
8123
87
],
9-
"postCreateCommand": "container install",
10-
"extensions": [
11-
"ms-python.python",
12-
"github.vscode-pull-request-github",
13-
"ryanluker.vscode-coverage-gutters",
14-
"ms-python.vscode-pylance"
15-
],
16-
"settings": {
17-
"files.eol": "\n",
18-
"editor.tabSize": 4,
19-
"terminal.integrated.shell.linux": "/bin/bash",
20-
"python.pythonPath": "/usr/bin/python3",
21-
"python.analysis.autoSearchPaths": false,
22-
"python.linting.pylintEnabled": true,
23-
"python.linting.enabled": true,
24-
"python.formatting.provider": "black",
25-
"editor.formatOnPaste": false,
26-
"editor.formatOnSave": true,
27-
"editor.formatOnType": true,
28-
"files.trimTrailingWhitespace": true
29-
}
30-
8+
"portsAttributes": {
9+
"8123": {
10+
"label": "Home Assistant",
11+
"onAutoForward": "notify"
12+
}
13+
},
14+
"customizations": {
15+
"vscode": {
16+
"extensions": [
17+
"charliermarsh.ruff",
18+
"github.vscode-pull-request-github",
19+
"ms-python.python",
20+
"ms-python.vscode-pylance",
21+
"ryanluker.vscode-coverage-gutters"
22+
],
23+
"settings": {
24+
"files.eol": "\n",
25+
"editor.tabSize": 4,
26+
"editor.formatOnPaste": true,
27+
"editor.formatOnSave": true,
28+
"editor.formatOnType": false,
29+
"files.trimTrailingWhitespace": true,
30+
"python.analysis.typeCheckingMode": "basic",
31+
"python.analysis.autoImportCompletions": true,
32+
"python.defaultInterpreterPath": "/usr/local/bin/python",
33+
"[python]": {
34+
"editor.defaultFormatter": "charliermarsh.ruff"
35+
}
36+
}
37+
}
38+
},
39+
"remoteUser": "vscode",
40+
"features": {}
3141
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ __pycache__/
66
# C extensions
77
*.so
88

9+
# Dont commit the config folder.
10+
config/
11+
912
# Distribution / packaging
1013
.Python
1114
build/

README.md

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MAXZPYVPD8XS6)
33
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/2ys3cdCZk)
44

5-
Nord Pool is a service provider that operates an electricity market and power system services, including the exchange of electricity on a spot market Nordics and Baltic countries.
5+
Nord Pool is a service provider that operates an electricity market and power system services, including the exchange of electricity on a spot market Nordics and Baltic countries.
66

7-
This integration provides the spot market (hourly) electricity prices for the Nordic, Baltic and part of Western Europe.
7+
This integration provides the spot market (hourly) electricity prices for the Nordic, Baltic and part of Western Europe.
88

99
The Nordpool sensor provides the current price with today's and tomorrow's prices as attributes. Prices become available around 13:00.
1010

@@ -31,8 +31,8 @@ The Nordpool sensor provides the current price with today's and tomorrow's price
3131
- Restart Home Assistant
3232

3333
*or*
34-
- Go to `HACS` -> `Integrations`,
35-
- Select `+`,
34+
- Go to `HACS` -> `Integrations`,
35+
- Select `+`,
3636
- Search for `nordpool` and install it,
3737
- Restart Home Assistant
3838

@@ -44,15 +44,15 @@ cd YOUR_HASS_CONFIG_DIRECTORY # same place as configuration.yaml
4444
mkdir -p custom_components/nordpool
4545
cd custom_components/nordpool
4646
unzip nordpool-X.Y.Z.zip
47-
mv nordpool-X.Y.Z/custom_components/nordpool/* .
47+
mv nordpool-X.Y.Z/custom_components/nordpool/* .
4848
```
4949

5050
## Usage
5151

5252
### Configuration Variables
53-
| Configuration | Required | Description |
53+
| Configuration | Required | Description |
5454
|----------------------| -------- | ----------------------------- |
55-
| Region | **yes** | Country/region to get the energy prices for. See Country/region codes below for details.|
55+
| Region | **yes** | Country/region to get the energy prices for. See Country/region codes below for details.|
5656
| Currency | no | *Default: local currency* <br> Currency used to fetch the prices from the API.|
5757
| Include VAT | no | *Default: true* <br> Add Value Added Taxes (VAT) or not.|
5858
| Decimal precision | no | *Default: 3* <br> Energy price rounding precision. |
@@ -76,25 +76,25 @@ Set up the sensor using in `configuration.yaml`.
7676
```yaml
7777
sensor:
7878
- platform: nordpool
79-
region: "NO1"
79+
region: "NO1"
8080
```
8181
8282
#### Example configuration:
8383
```yaml
8484
sensor:
8585
- platform: nordpool
86-
# Country/region to get the energy prices for.
86+
# Country/region to get the energy prices for.
8787
region: "NO1"
88-
88+
8989
# Override HA local currency used to fetch the prices from the API.
9090
currency: "EUR"
91-
91+
9292
# Add Value Added Taxes (VAT)?
9393
VAT: True
94-
94+
9595
# Energy price rounding precision.
9696
precision: 3
97-
97+
9898
# Percentage of average price to set the low price attribute
9999
# low_price = hour_price < average * low_price_cutoff
100100
low_price_cutoff: 0.95
@@ -107,13 +107,13 @@ sensor:
107107

108108
# Template to specify additional cost to be added to the tariff.
109109
# The template price is in EUR, DKK, NOK or SEK (not in cents).
110-
# For example: "{{ current_price * 0.19 + 0.023 | float}}"
110+
# For example: "{{ current_price * 0.19 + 0.023 | float}}"
111111
additional_costs: "{{0.0|float}}"
112112
```
113113
### Regions
114114
See the [Nord Pool region map](https://www.nordpoolgroup.com/en/maps/) for details
115115
116-
| Country | Region code |
116+
| Country | Region code |
117117
| --------- | ----------- |
118118
| Austria | AT |
119119
| Belgium | BE |
@@ -132,30 +132,30 @@ See the [Nord Pool region map](https://www.nordpoolgroup.com/en/maps/) for detai
132132
| Sweden | SE1, <br> SE2, <br> SE3, <br> SE4 |
133133
134134
### Additional costs
135-
The idea behind `additional_costs` is to allow the users to add costs related to the official price from Nordpool:
135+
The idea behind `additional_costs` is to allow the users to add costs related to the official price from Nordpool:
136136
- Add simple or complex tariffs
137137
- Calculate VAT
138138

139139
There are two special special arguments in that can be used in the template ([in addition to all default from Homeassistant](https://www.home-assistant.io/docs/configuration/templating/)):
140140
- ```now()```: this always refer to the current hour of the price
141-
- ```current_price```: price for the current hour. This can be used for example be used to calculate your own VAT or add overhead cost.
141+
- ```current_price```: price for the current hour. This can be used for example be used to calculate your own VAT or add overhead cost.
142142

143143
Note: When configuring Nordpool using the UI, things like VAT and additional costs cannot be changed. If your energy supplier or region changes the additional costs or taxes on a semi-regular basis, the YAML configuration or a helper (example 4) work best.
144144

145145
#### Example 1: Overhead per kWh
146146

147-
Add 1,3 cents per kWh overhead cost to the current hour's price
147+
Add 1,3 cents per kWh overhead cost to the current hour's price
148148

149149
```{{ 0.013 | float }}```
150150

151151
#### Example 2: Percentage (VAT)
152-
Add 19 % VAT of the current hour's price
152+
Add 19 % VAT of the current hour's price
153153

154154
```{{ (current_price * 0.19) | float }}```
155155

156156
#### Example 3: Overhead and VAT
157157

158-
Add 1,3 cents per kWh overhead cost, 0.002 flat tax and 19% VAT to the current hour's price
158+
Add 1,3 cents per kWh overhead cost, 0.002 flat tax and 19% VAT to the current hour's price
159159

160160
```{{ (0.013 + 0.002 + (current_price * 0.19)) | float }}```
161161

@@ -208,7 +208,7 @@ Add 21% tax and overhead cost stored in a helper
208208
- ```country```: What Country data is fetched for
209209
- ```region```: The specific region of prices
210210
- ```low_price```: If price is below low_price_threshold
211-
- ```price_percent_to_average```:
211+
- ```price_percent_to_average```:
212212
- ```today```: List of all values
213213
- ```tomorrow```: list of all values
214214
- ```tomorrow_valid```: If tomorow´s values is in yet
@@ -218,9 +218,27 @@ Add 21% tax and overhead cost stored in a helper
218218
- ```additional_costs_current_hour```: If there is any additional costs this hour
219219
- ```price_in_cents```: Boolean if prices is in cents
220220

221-
### One sensor per hour
221+
## Actions
222+
Actions has recently been added. The action will just forward the raw response from the Nordpool API so you can capture the value your are interested in.
222223

223-
By default, one sensor is created with the current energy price. The prices for other hours are stored in the attributes of this sensor. Most example code you will find uses the default one sensor option, but you can run the `create_template` script to create separate sensors for every hour. See the help options with ```python create_template --help```. You can run the script on any system where Python is installed (install the required packages `pyyaml` and `click` using `pip install pyyaml click`)
224+
Example for an automation that get the last months averge price.
225+
```yaml
226+
alias: Example automation action call with storing with parsing and storing result
227+
triggers: null
228+
actions:
229+
- action: nordpool.yearly
230+
data:
231+
currency: NOK
232+
area: NO2
233+
year: "2024"
234+
response_variable: np_result
235+
- action: input_text.set_value
236+
target:
237+
entity_id: input_text.test
238+
data:
239+
value: "{{np_result.prices[0].averagePerArea.NO2 | float}}"
240+
mode: single
241+
```
224242

225243
## Troubleshooting
226244

custom_components/nordpool/__init__.py

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import logging
22
from collections import defaultdict
33
from datetime import timedelta
4-
from random import randint
4+
55

66
import backoff
7-
import voluptuous as vol
87
from homeassistant.config_entries import ConfigEntry
98
from homeassistant.core import HomeAssistant
109
from homeassistant.const import Platform
@@ -16,25 +15,21 @@
1615

1716
from .aio_price import AioPrices, InvalidValueException
1817
from .events import async_track_time_change_in_tz
18+
from .services import async_setup_services
19+
20+
from .const import (
21+
NAME,
22+
VERSION,
23+
ISSUEURL,
24+
DOMAIN,
25+
EVENT_NEW_DAY,
26+
EVENT_NEW_HOUR,
27+
EVENT_NEW_PRICE,
28+
_CURRENCY_LIST,
29+
RANDOM_MINUTE,
30+
RANDOM_SECOND,
31+
)
1932

20-
DOMAIN = "nordpool"
21-
_LOGGER = logging.getLogger(__name__)
22-
RANDOM_MINUTE = randint(10, 30)
23-
RANDOM_SECOND = randint(0, 59)
24-
EVENT_NEW_HOUR = "nordpool_update_hour"
25-
EVENT_NEW_DAY = "nordpool_update_day"
26-
EVENT_NEW_PRICE = "nordpool_update_new_price"
27-
SENTINEL = object()
28-
29-
_CURRENCY_LIST = ["DKK", "EUR", "NOK", "SEK"]
30-
31-
32-
CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)
33-
34-
35-
NAME = DOMAIN
36-
VERSION = "0.0.16"
37-
ISSUEURL = "https://github.com/custom-components/nordpool/issues"
3833

3934
STARTUP = f"""
4035
-------------------------------------------------------------------
@@ -46,6 +41,8 @@
4641
-------------------------------------------------------------------
4742
"""
4843

44+
_LOGGER = logging.getLogger(__name__)
45+
4946
PLATFORMS: list[Platform] = [Platform.SENSOR]
5047

5148

@@ -76,7 +73,9 @@ async def _update(self, type_="today", dt=None, areas=None):
7673
# Keeping this for now, but this should be changed.
7774
for currency in self.currency:
7875
spot = AioPrices(currency, client)
79-
data = await spot.hourly(end_date=dt, areas=self.areas if len(self.areas) > 0 else None)
76+
data = await spot.hourly(
77+
end_date=dt, areas=self.areas if len(self.areas) > 0 else None
78+
)
8079
if data:
8180
self._data[currency][type_] = data["areas"]
8281

@@ -92,7 +91,11 @@ async def update_tomorrow(self, areas=None):
9291
_LOGGER.debug("Updating tomorrows prices.")
9392
if areas is not None:
9493
self.areas += [area for area in areas if area not in self.areas]
95-
await self._update(type_="tomorrow", dt=dt_utils.now() + timedelta(hours=24), areas=self.areas if len(self.areas) > 0 else None)
94+
await self._update(
95+
type_="tomorrow",
96+
dt=dt_utils.now() + timedelta(hours=24),
97+
areas=self.areas if len(self.areas) > 0 else None,
98+
)
9699

97100
async def _someday(self, area: str, currency: str, day: str):
98101
"""Returns today's or tomorrow's prices in an area in the currency"""
@@ -103,7 +106,7 @@ async def _someday(self, area: str, currency: str, day: str):
103106
)
104107

105108
if area not in self.areas:
106-
self.areas.append(area);
109+
self.areas.append(area)
107110
# This is needed as the currency is
108111
# set in the sensor.
109112
if currency not in self.currency:
@@ -126,19 +129,19 @@ async def _someday(self, area: str, currency: str, day: str):
126129
async def today(self, area: str, currency: str) -> dict:
127130
"""Returns today's prices in an area in the requested currency"""
128131
return await self._someday(area, currency, "today")
129-
130132

131133
async def tomorrow(self, area: str, currency: str):
132134
"""Returns tomorrow's prices in an area in the requested currency"""
133135
return await self._someday(area, currency, "tomorrow")
134-
136+
135137

136138
async def _dry_setup(hass: HomeAssistant, config: ConfigType) -> bool:
137139
"""Set up using yaml config file."""
138140
if DOMAIN not in hass.data:
139141
api = NordpoolData(hass)
140142
hass.data[DOMAIN] = api
141143
_LOGGER.debug("Added %s to hass.data", DOMAIN)
144+
await async_setup_services(hass)
142145

143146
async def new_day_cb(_):
144147
"""Cb to handle some house keeping when it a new day."""
@@ -161,7 +164,11 @@ async def new_hr(_):
161164
@backoff.on_exception(
162165
backoff.constant,
163166
(InvalidValueException),
164-
logger=_LOGGER, interval=600, max_time=7200, jitter=None)
167+
logger=_LOGGER,
168+
interval=600,
169+
max_time=7200,
170+
jitter=None,
171+
)
165172
async def new_data_cb(_):
166173
"""Callback to fetch new data for tomorrows prices at 1300ish CET
167174
and notify any sensors, about the new data

0 commit comments

Comments
 (0)