Skip to content
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

Function to fetch historical data added and optimized Peak Power code #31

Merged
merged 8 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist/
__pycache__/
pipx_shared.pth
pipx_shared.pth
cw_test.py
88 changes: 88 additions & 0 deletions examples/history_inject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Pulls historical data from CheckWatt EnergyInBalance and push it to CheckWattRank"""

import aiohttp

from pycheckwatt import CheckwattManager

START_DATE = "2023-09-01"
END_DATE = "2024-01-31"

EIB_USERNAME = ""
EIB_PASSWORD = ""
DISPLAY_NAME_OVERRIDE = ""

BASE_URL = "https://checkwattrank.netlify.app/"


async def main():
"""Main function."""
if EIB_USERNAME == "" or EIB_PASSWORD == "":
print("You need to update EIB_USERNAME/EIB_PASSWORD")
return

async with CheckwattManager(EIB_USERNAME, EIB_PASSWORD, "cwTest") as cw:
try:
# Login to EnergyInBalance
if await cw.login():
# Fetch customer detail
if not await cw.get_customer_details():
print("Failed to fetch customer details")
return

if not await cw.get_price_zone():
print("Failed to fetch prize zone")
return

hd = await cw.fetch_and_return_net_revenue(START_DATE, END_DATE)
if hd is None:
print("Failed to fetch revenues")
return

energy_provider = await cw.get_energy_trading_company(
cw.energy_provider_id
)
if energy_provider is None:
print("Failed to fetch electricity compan")
return

data = {
"display_name": (
DISPLAY_NAME_OVERRIDE
if DISPLAY_NAME_OVERRIDE != ""
else cw.display_name
),
"dso": cw.battery_registration["Dso"],
"electricity_area": cw.price_zone,
"installed_power": cw.battery_charge_peak_ac,
"electricity_company": energy_provider,
"reseller_id": cw.reseller_id,
"reporter": "CheckWattRank",
"historical_data": hd,
}

# Post data to Netlify function
netlify_function_url = BASE_URL + "/.netlify/functions/publishHistory"
async with aiohttp.ClientSession() as session:
async with session.post(
netlify_function_url, json=data
) as response:
if response.status == 200:
result = await response.json()
count = result.get("count", 0)
total = result.get("total", 0)
print(f"Data posted successfully. Count: {count}/{total}")
else:
print(
f"Failed to post data. Status code: {response.status}"
)

except Exception as e:
print(f"An error occurred: {e}")


if __name__ == "__main__":
import asyncio

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())
139 changes: 86 additions & 53 deletions pycheckwatt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import json
import logging
import re
from datetime import datetime, timedelta
from datetime import date, datetime, timedelta

from aiohttp import ClientError, ClientResponseError, ClientSession
from dateutil.relativedelta import relativedelta
Expand Down Expand Up @@ -68,6 +68,9 @@ def __init__(self, username, password, application="pyCheckwatt") -> None:
self.header_identifier = application
self.rpi_data = None
self.meter_data = None
self.display_name = None
self.reseller_id = None
self.energy_provider_id = None

async def __aenter__(self):
"""Asynchronous enter."""
Expand Down Expand Up @@ -227,7 +230,7 @@ async def login(self):

if response.status == 401:
_LOGGER.error(
"Unauthorized: Check your checkwatt authentication credentials"
"Unauthorized: Check your CheckWatt authentication credentials"
)
return False

Expand Down Expand Up @@ -269,6 +272,11 @@ async def get_customer_details(self):
if not soc_meter:
_LOGGER.error("No SoC meter found")
return False

self.display_name = soc_meter.get("DisplayName")
self.reseller_id = soc_meter.get("ResellerId")
self.energy_provider_id = soc_meter.get("ElhandelsbolagId")

logbook = soc_meter.get("Logbook")
if logbook:
(
Expand All @@ -277,38 +285,6 @@ async def get_customer_details(self):
) = self._extract_content_and_logbook(logbook)
self._extract_fcr_d_state()

return True

_LOGGER.error(
"Obtaining data from URL %s failed with status code %d",
self.base_url + endpoint,
response.status,
)
return False

except (ClientResponseError, ClientError) as error:
return await self.handle_client_error(endpoint, headers, error)

async def get_battery_peak_data(self):
"""Fetch battery peak data from CheckWatt."""
try:
endpoint = "/controlpanel/CustomerDetail"

# Define headers with the JwtToken
headers = {
**self._get_headers(),
"authorization": f"Bearer {self.jwt_token}",
}

async with self.session.get(
self.base_url + endpoint, headers=headers
) as response:
response.raise_for_status()
if response.status == 200:
self.customer_details = await response.json()

meters = self.customer_details.get("Meter", [])
if meters:
charging_meter = next(
(
meter
Expand All @@ -317,27 +293,25 @@ async def get_battery_peak_data(self):
),
None,
)
discharging_meter = next(
if charging_meter:
self.battery_charge_peak_ac = charging_meter.get("PeakAcKw")
self.battery_charge_peak_dc = charging_meter.get("PeakDcKw")

discharge_meter = next(
(
meter
for meter in meters
if meter.get("InstallationType") == "Discharging"
),
None,
)

if not charging_meter:
_LOGGER.error("No charging meter found")
return False
self.battery_charge_peak_ac = charging_meter.get("PeakAcKw")
print(self.battery_charge_peak_ac)
self.battery_charge_peak_dc = charging_meter.get("PeakDcKw")
self.battery_discharge_peak_ac = discharging_meter.get(
"PeakAcKw"
)
self.battery_discharge_peak_dc = discharging_meter.get(
"PeakDcKw"
)
if discharge_meter:
self.battery_discharge_peak_ac = discharge_meter.get(
"PeakAcKw"
)
self.battery_discharge_peak_dc = discharge_meter.get(
"PeakDcKw"
)

return True

Expand All @@ -352,7 +326,7 @@ async def get_battery_peak_data(self):
return await self.handle_client_error(endpoint, headers, error)

async def get_fcrd_month_net_revenue(self):
"""Fetch FCR-D revenues from checkwatt."""
"""Fetch FCR-D revenues from CheckWatt."""
misseddays = 0
try:
from_date = datetime.now().strftime("%Y-%m-01")
Expand Down Expand Up @@ -410,7 +384,7 @@ async def get_fcrd_month_net_revenue(self):
return await self.handle_client_error(endpoint, headers, error)

async def get_fcrd_today_net_revenue(self):
"""Fetch FCR-D revenues from checkwatt."""
"""Fetch FCR-D revenues from CheckWatt."""
try:
from_date = datetime.now().strftime("%Y-%m-%d")
end_date = datetime.now() + timedelta(days=2)
Expand Down Expand Up @@ -514,6 +488,65 @@ async def get_fcrd_year_net_revenue(self):
except (ClientResponseError, ClientError) as error:
return await self.handle_client_error(endpoint, headers, error)

async def fetch_and_return_net_revenue(self, from_date, to_date):
"""Fetch FCR-D revenues from CheckWatt as per provided range."""
try:
# Validate date format and ensure they are dates
date_format = "%Y-%m-%d"
try:
from_date = datetime.strptime(from_date, date_format).date()
to_date = datetime.strptime(to_date, date_format).date()
except ValueError:
raise ValueError(
"Input dates must be valid dates with the format YYYY-MM-DD."
)

# Validate from_date and to_date
today = date.today()
six_months_ago = today - relativedelta(months=6)

if not (six_months_ago <= from_date <= today):
raise ValueError(
"From date must be within the last 6 months and not beyond today."
)

if not (six_months_ago <= to_date <= today):
raise ValueError(
"To date must be within the last 6 months and not beyond today."
)

if from_date >= to_date:
raise ValueError("From date must be before To date.")

# Extend to_date by one day
to_date += timedelta(days=1)

endpoint = f"/ems/revenue?fromDate={from_date}&toDate={to_date}"

# Define headers with the JwtToken
headers = {
**self._get_headers(),
"authorization": f"Bearer {self.jwt_token}",
}
# First fetch the revenue
async with self.session.get(
self.base_url + endpoint, headers=headers
) as response:
response.raise_for_status()
revenue = await response.json()
if response.status == 200:
return revenue

_LOGGER.error(
"Obtaining data from URL %s failed with status code %d",
self.base_url + endpoint,
response.status,
)
return None

except (ClientResponseError, ClientError) as error:
return await self.handle_client_error(endpoint, headers, error)

def _build_series_endpoint(self, grouping):
end_date = datetime.now() + timedelta(days=2)
to_date = end_date.strftime("%Y")
Expand All @@ -531,7 +564,7 @@ def _build_series_endpoint(self, grouping):
return None

async def get_power_data(self):
"""Fetch Power Data from checkwatt."""
"""Fetch Power Data from CheckWatt."""

try:
endpoint = self._build_series_endpoint(
Expand Down Expand Up @@ -595,7 +628,7 @@ async def get_energy_flow(self):
return await self.handle_client_error(endpoint, headers, error)

async def get_price_zone(self):
"""Fetch Price Zone from checkwatt."""
"""Fetch Price Zone from CheckWatt."""

try:
endpoint = "/ems/pricezone"
Expand Down Expand Up @@ -625,7 +658,7 @@ async def get_price_zone(self):
return await self.handle_client_error(endpoint, headers, error)

async def get_spot_price(self):
"""Fetch Spot Price from checkwatt."""
"""Fetch Spot Price from CheckWatt."""

try:
from_date = datetime.now().strftime("%Y-%m-%d")
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pycheckwatt"
version = "0.2.0"
version = "0.2.1"
description = "Read data from CheckWatts EnergyInBalance WEB API"
authors = ["Marcus Karlsson <[email protected]>", "Anders Yderborg <[email protected]>", "Daniel Nilsson <[email protected]>"]
license = "MIT License"
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

MIN_PY_VERSION = "3.10"
PACKAGES = find_packages()
VERSION = "0.2.0"
VERSION = "0.2.1"

setup(
name="pycheckwatt",
Expand Down
Loading