Skip to content
Draft
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
Expand Up @@ -9,4 +9,5 @@ env
.coverage
coverage.xml
.secrets
.pytest_cache
.pytest_cache
node_modules
72 changes: 71 additions & 1 deletion custom_components/postnl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import urllib3
from aiohttp.client_exceptions import ClientError, ClientResponseError
from gql.transport.exceptions import TransportQueryError
from homeassistant.components.frontend import add_extra_js_url
from homeassistant.components.http import StaticPathConfig
from homeassistant.components.lovelace.resources import ResourceStorageCollection
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.core import HomeAssistant
Expand All @@ -14,7 +17,7 @@
from homeassistant.helpers.config_entry_oauth2_flow import (
OAuth2Session, async_get_config_entry_implementation)

from .const import DOMAIN, PLATFORMS
from .const import DOMAIN, PLATFORMS, VERSION
from .graphql import PostNLGraphql
from .login_api import PostNLLoginAPI

Expand Down Expand Up @@ -83,6 +86,73 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> True:

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

# if not hass.http.app.router.get('/postnl/lovelace'):
# hass.http.register_static_path(
# url_path='/postnl/lovelace',
# path=hass.config.path("custom_components/postnl/lovelace"),
# cache_headers=True
# )

if not await path_registered(hass=hass, url='/postnl/lovelace.js'):
await hass.http.async_register_static_paths([
StaticPathConfig(
url_path='/postnl/lovelace.js',
path=hass.config.path("custom_components/postnl/lovelace.js"),
cache_headers=True
)
])

await init_resource(
hass=hass,
url='/postnl/lovelace.js',
ver=VERSION
)

return True


async def path_registered(hass: HomeAssistant, url: str):
for resource in hass.http.app.router.resources():
if url in resource.canonical:
return True

return False


async def init_resource(hass: HomeAssistant, url: str, ver: str) -> bool:
resources: ResourceStorageCollection = hass.data["lovelace"]["resources"]
# force load storage
await resources.async_get_info()

url2 = f"{url}?v={ver}"

for item in resources.async_items():
if not item.get("url", "").startswith(url):
continue

# no need to update
if item["url"].endswith(ver):
return False

_LOGGER.debug(f"Update lovelace resource to: {url2}")

if isinstance(resources, ResourceStorageCollection):
await resources.async_update_item(
item["id"], {"res_type": "module", "url": url2}
)
else:
# not the best solution, but what else can we do
item["url"] = url2

return True

if isinstance(resources, ResourceStorageCollection):
_LOGGER.debug(f"Add new lovelace resource: {url2}")
await resources.async_create_item({"res_type": "module", "url": url2})
else:
_LOGGER.debug(f"Add extra JS module: {url2}")
add_extra_js_url(hass, url2)

return True


Expand Down
7 changes: 4 additions & 3 deletions custom_components/postnl/application_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class OAuth2Impl(AuthImplementation):
code_verifier: str | None

def __init__(self, hass: HomeAssistant, auth_domain: str, credential: ClientCredential,
authorization_server: AuthorizationServer, code_challenge: str | None, code_verifier: str | None) -> None:

authorization_server: AuthorizationServer, code_challenge: str | None,
code_verifier: str | None) -> None:
super().__init__(hass, auth_domain, credential, authorization_server)

self.code_verifier = code_verifier
Expand Down Expand Up @@ -53,8 +53,9 @@ async def async_resolve_external_data(self, external_data: Any) -> dict:
}
)


async def async_get_auth_implementation(
hass: HomeAssistant, auth_domain: str, credential: ClientCredential
hass: HomeAssistant, auth_domain: str, credential: ClientCredential
) -> config_entry_oauth2_flow.AbstractOAuth2Implementation:
"""Return auth implementation for a custom auth implementation."""

Expand Down
49 changes: 49 additions & 0 deletions custom_components/postnl/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import logging

import voluptuous
import voluptuous as vol
import homeassistant.helpers.config_validation as cv

from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers import config_entry_oauth2_flow

from .const import DOMAIN
from .dhl import DHL

_LOGGER = logging.getLogger(__name__)


class OAuth2FlowHandler(
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
):
Expand All @@ -21,6 +27,49 @@ def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)

async def async_step_user(self, info = None):
_LOGGER.debug(info)
if info is not None:
if "DHL" in info.get('providers'):
return await self.async_step_dhl(info)
else:
return await super().async_step_user()

return self.async_show_form(
step_id="user", data_schema=vol.Schema({
vol.Required("providers"): vol.All(
cv.multi_select([
"PostNL",
"DHL"
])
)
})
)

async def async_step_dhl(self, info = None):
errors = {}
_LOGGER.debug(info)
if info.get('email', None) is not None:
dhl = DHL()
response = await self.hass.async_add_executor_job(dhl.login,
info.get('email'),
info.get('password')
)
_LOGGER.debug('DHL login: %s', response)

if response.get('userId', None) is None:
errors["base"] = "invalid_auth"


return self.async_show_form(
step_id="dhl",
data_schema=vol.Schema({
vol.Required("email"): str,
vol.Required("password"): str
}),
errors=errors
)

async def async_step_reauth(self, user_input=None):
"""Perform reauth upon an API authentication error."""
self.reauth_entry = self.hass.config_entries.async_get_entry(
Expand Down
4 changes: 2 additions & 2 deletions custom_components/postnl/const.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from homeassistant.const import Platform

DOMAIN = "postnl"
VERSION = "1.4.0"
POSTNL_CLIENT_ID = "deb0a372-6d72-4e09-83fe-997beacbd137"
POSTNL_AUTH_URL = "https://login.postnl.nl/101112a0-4a0f-4bbb-8176-2f1b2d370d7c/login/authorize"
POSTNL_TOKEN_URL = "https://login.postnl.nl/101112a0-4a0f-4bbb-8176-2f1b2d370d7c/login/token"
POSTNL_REDIRECT_URI = "postnl://login"
POSTNL_SCOPE = "profile openid email address phone poa-profiles-api"


PLATFORMS = [
Platform.SENSOR
]
]
42 changes: 42 additions & 0 deletions custom_components/postnl/dhl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import logging

import requests
from requests.adapters import HTTPAdapter
from urllib3 import Retry

_LOGGER = logging.getLogger(__name__)


class DHL:
base_url: str = "https://my.dhlecommerce.nl/"

def __init__(self):
self.client = requests.Session()
self.client.mount(
prefix='https://',
adapter=HTTPAdapter(
max_retries=Retry(
total=5,
backoff_factor=3
)
)
)

def login(self, username: str, password: str):
return self.client.post(
url=self.base_url + 'api/user/login',
json={
'email': username,
'password': password
}
).json()

def user(self):
return self.client.get(
url=self.base_url + 'api/user',
).json()

def parcels(self):
return self.client.get(
url=self.base_url + 'receiver-parcel-api/parcels',
).json()
Loading