Skip to content

Commit

Permalink
Fix API integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Jurkash committed Jul 14, 2024
1 parent 4d89ab4 commit 443232b
Show file tree
Hide file tree
Showing 16 changed files with 303 additions and 159 deletions.
34 changes: 34 additions & 0 deletions .devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "ha-loe-outages",
"image": "mcr.microsoft.com/devcontainers/python:3.12-bullseye",
"postCreateCommand": "scripts/setup",
"forwardPorts": [
8123
],
"portsAttributes": {
"8123": {
"label": "Home Assistant",
"onAutoForward": "notify"
}
},
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"github.vscode-pull-request-github",
"ryanluker.vscode-coverage-gutters",
"ms-python.vscode-pylance",
"charliermarsh.ruff",
"tamasfe.even-better-toml"
],
"settings": {
"python.pythonPath": "/usr/bin/python3",
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "charliermarsh.ruff"
}
}
}
},
"remoteUser": "vscode"
}
35 changes: 1 addition & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
<!-- ![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://stand-with-ukraine.pp.ua/)
-->
![HA LOE Outages Logo](./icons/logo.svg)

# ⚡️ HA LOE Outages
Expand Down Expand Up @@ -36,45 +34,15 @@ If the button doesn't work, follow these steps to add the repository manually:

1. Go to **HACS****Integrations****...** (top right) → **Custom repositories**
2. Click **Add**
3. Enter `https://github.com/jurakash/ha-loe-outages` in the **URL** field
3. Enter `https://github.com/jurkash/ha-loe-outages` in the **URL** field
4. Choose **Integration** as the **Category**
5. **LOE Outages** will appear in the list of available integrations. Install it as usual.

<!--
## Usage
This integration can be configured via the UI. On the **Devices and Services** page, click **Add Integration** and search for **LOE Outages**.
Find your group by visiting the [LOE][loe] website and entering your address in the search bar. Select your group in the configuration.
![Configuration flow](https://github.com/jurkash/ha-loe-outages/assets/3459374/e8bfde50-fcbe-45c3-b448-b451b0ac3bcd)
After configuring, add the integration to your dashboard to view the next planned outages.
![Device page](https://github.com/jurkash/ha-loe-outages/assets/3459374/df628647-fd2a-455d-9d08-0d1542b67e41)
The integration also provides a calendar view of planned outages, which can be added to your dashboard via the [Calendar card][calendar-card].
![Calendar view](https://github.com/jurkash/ha-loe-outages/assets/3459374/b09c4db3-d0a0-4e06-8dd9-3f4a59f1d63e)
Here’s an example of a dashboard using this integration:
![Dashboard example](https://github.com/jurkash/ha-loe-outages/assets/3459374/26c75595-8984-4a9f-893a-e4b6d838b7f2) -->

<!-- ## Development
Interested in contributing to the project?
First, thank you! Check out the [contributing guideline](./CONTRIBUTING.md) for more information. -->


## License

MIT © [Yurii Shunkin][jurkash]

<!-- Badges -->

[gh-release-url]: https://github.com/jurkash/ha-loe-outages/releases/latest
[gh-release-image]: https://img.shields.io/github/v/release/jurkash/ha-loe-outages?style=flat-square
[gh-downloads-url]: https://github.com/jurkash/ha-loe-outages/releases
Expand All @@ -91,7 +59,6 @@ MIT © [Yurii Shunkin][jurkash]
[twitter-image]: https://img.shields.io/badge/twitter-%40jurkashok-00ACEE.svg?style=flat-square

<!-- References -->

[loe]: https://poweron.loe.lviv.ua/
[home-assistant]: https://www.home-assistant.io/
[jurkash]: https://github.com/jurkash
Expand Down
4 changes: 2 additions & 2 deletions custom_components/loe_outages/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Init file for LOE Outages integration."""
"""Init file for Loe Outages integration."""

from __future__ import annotations

Expand Down Expand Up @@ -38,4 +38,4 @@ async def async_unload_entry(
"""Handle removal of an entry."""
LOGGER.info("Unload entry: %s", entry)
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
104 changes: 65 additions & 39 deletions custom_components/loe_outages/api.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,82 @@
"""API for LOE outages."""
"""API for Loe outages."""

import datetime
import logging
import requests

from .const import API_BASE_URL
import aiohttp
from .models import OutageSchedule
import datetime
import pytz

LOGGER = logging.getLogger(__name__)


class LoeOutagesApi:
"""Class to interact with the API for LOE outages."""
"""Class to interact with API for Loe outages."""

schedule: list[OutageSchedule]

def __init__(self, group: str) -> None:
"""Initialize the LOE OutagesApi."""
"""Initialize the LoeOutagesApi."""
self.group = group
self.api_base_url = API_BASE_URL

def fetch_schedule(self) -> list[dict]:
"""Fetch outages from the API."""
url = f"{self.api_base_url}/Schedule/latest"
response = requests.get(url)
response.raise_for_status()
data = response.json()
for group in data["groups"]:
if group["id"] == self.group:
return group["intervals"]
return []

def get_current_event(self, at: datetime.datetime) -> dict:
self.schedule = []

async def async_fetch_json_from_endpoint(self) -> dict:
"""Fetch outages from the async API endpoint."""
url = "https://lps.yuriishunkin.com/api/Schedule/latest"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
data = await response.json()
return data
else:
LOGGER.error(f"Failed to fetch schedule: {response.status}")
return None

async def async_fetch_schedule(self) -> None:
"""Fetch outages from the JSON response."""
schedule_data = await self.async_fetch_json_from_endpoint()
schedule = OutageSchedule.from_dict(schedule_data)
if len(self.schedule) == 0:
self.schedule.append(schedule)
return

if self.schedule[-1].id != schedule.id:
if self.schedule[-1].dateString == schedule.dateString:
self.schedule.remove(self.schedule[-1])
self.schedule.append(schedule)

def get_current_event(self, at: datetime) -> dict:
"""Get the current event."""
schedule = self.fetch_schedule()
current_event = None
for event in schedule:
start = datetime.datetime.fromisoformat(event["startTime"])
end = datetime.datetime.fromisoformat(event["endTime"])
if start <= at <= end:
current_event = event
break
return current_event
if not self.schedule:
return None

twoDaysBefore = datetime.datetime.now() + datetime.timedelta(days=-2)
for schedule in reversed(self.schedule):
if schedule.date < twoDaysBefore.astimezone(pytz.UTC):
return None

events_at = schedule.get_current_event(self.group, at)
if not events_at:
return None
return events_at # return only the first event

def get_events(
self,
start_date: datetime.datetime,
end_date: datetime.datetime,
) -> list[dict]:
"""Get all events between start_date and end_date."""
schedule = self.fetch_schedule()
events = []
for event in schedule:
start = datetime.datetime.fromisoformat(event["startTime"])
end = datetime.datetime.fromisoformat(event["endTime"])
if start_date <= start <= end_date or start_date <= end <= end_date:
events.append(event)
return events
"""Get all events."""
if not self.schedule:
return []

result = []
twoDaysBeforeStart = start_date + datetime.timedelta(days=-2)
for schedule in reversed(self.schedule):
if schedule.date < twoDaysBeforeStart:
break

for interval in schedule.between(self.group, start_date, end_date):
result.append(interval)

return result


25 changes: 21 additions & 4 deletions custom_components/loe_outages/calendar.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Calendar platform for LOE outages integration."""
"""Calendar platform for Loe outages integration."""

import datetime
import logging
Expand All @@ -21,7 +21,7 @@ async def async_setup_entry(
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the LOE outages calendar platform."""
"""Set up the Loe outages calendar platform."""
LOGGER.debug("Setup new entry: %s", config_entry)
coordinator: LoeOutagesCoordinator = config_entry.runtime_data
async_add_entities([LoeOutagesCalendar(coordinator)])
Expand Down Expand Up @@ -52,7 +52,13 @@ def event(self) -> CalendarEvent | None:
"""Return the current or next upcoming event or None."""
now = dt_utils.now()
LOGGER.debug("Getting current event for %s", now)
return self.coordinator.get_event_at(now)
interval = self.coordinator.get_event_at(now)
return CalendarEvent(
summary=interval.state,
start=interval.startTime,
end=interval.endTime,
description=interval.state,
)

async def async_get_events(
self,
Expand All @@ -62,4 +68,15 @@ async def async_get_events(
) -> list[CalendarEvent]:
"""Return calendar events within a datetime range."""
LOGGER.debug('Getting all events between "%s" -> "%s"', start_date, end_date)
return self.coordinator.get_events_between(start_date, end_date)
intervals = self.coordinator.get_events_between(start_date, end_date)
events = []

for interval in intervals:
event = CalendarEvent(
summary=interval.state,
start=interval.startTime,
end=interval.endTime,
description=interval.state,
)
events.append(event)
return events
8 changes: 4 additions & 4 deletions custom_components/loe_outages/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Config flow for LOE Outages integration."""
"""Config flow for Loe Outages integration."""

import logging
from typing import Any
Expand Down Expand Up @@ -52,7 +52,7 @@ def build_schema(config_entry: ConfigEntry) -> vol.Schema:


class LoeOutagesOptionsFlow(OptionsFlow):
"""Handle options flow for LOE Outages."""
"""Handle options flow for Loe Outages."""

def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
Expand All @@ -71,7 +71,7 @@ async def async_step_init(self, user_input: dict | None = None) -> ConfigFlowRes


class LoeOutagesConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for LOE Outages."""
"""Handle a config flow for Loe Outages."""

VERSION = 1

Expand All @@ -90,4 +90,4 @@ async def async_step_user(self, user_input: dict | None = None) -> ConfigFlowRes
return self.async_show_form(
step_id="user",
data_schema=build_schema(config_entry=None),
)
)
9 changes: 3 additions & 6 deletions custom_components/loe_outages/const.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Constants for the LOE Outages integration."""
"""Constants for the Loe Outages integration."""

from typing import Final

DOMAIN: Final = "loe_outages"
NAME: Final = "LOE Outages"
NAME: Final = "Loe Outages"

# Configuration option
CONF_GROUP: Final = "group"
Expand All @@ -17,10 +17,7 @@
# Values
STATE_ON: Final = "PowerOn"
STATE_OFF: Final = "PowerOff"
# Endpoint paths
SCHEDULE_PATH = "https://lps.yuriishunkin.com/api/schedule/latest/{group}"
API_BASE_URL = "https://lps.yuriishunkin.com/api"


# Keys
TRANSLATION_KEY_EVENT_OFF: Final = f"component.{DOMAIN}.common.electricity_off"
TRANSLATION_KEY_EVENT_ON: Final = f"component.{DOMAIN}.common.electricity_on"
Loading

0 comments on commit 443232b

Please sign in to comment.