-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: First try to fetch data * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix code: to meet Pre-commit requirements * fix: pre-commit guidelines, step 2 * fix: pre-commit guidelines, step 3 * fix: pre-commit errors step 4 * fix: pre-commit step 5 * fix: PR-commit 😡, try 6 * fix: pre-commit step 7, vamooooooooosss * fix: pre-commit step 8 * fix: pre-commit step 9 * fix: pre-commit, sorting imports and froms 🥴, step 10 * fix: pre-commit formatting froms * fix: pre-commit, como sea esto me cagontó porque la línea no era tan larga * fix: pre-commit step 11 * fix: pre-commit step 12 * feat: adding isort to pre-commit * me rindoooooooooooooooo * fix: adding issort to pyproject.toml * Fix imports * Fix imports * venga, a ver mezclando imports con froms * Poetry en pre-commit * Updated version * try again * poetry * poetry * poetry * poetry * Update pre-commit * fuera ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * pre-commit, fighting again * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * 😭 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * the final one? * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * another try * bug: missing DOMAIN in ConfigFlow * Update sensors as stats. Sum all value of the month * Update pre-commit Refactor APP_KEY y APP_SECRET * Fix values * patch: add aliexpress_api inside the project * patch * patch * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * include api * include api * remove precommit from api * ignore pre-commit for api * feat: use internal API handler (#4) * feat: use internal API handler * feat/use of Aliexpress SDK * pre-commit fixes * pre-commit fix * precommit * pc * pc * pre-commit fix * pc * pc * pc * pc * pc * pc * pc * pc * pc * pc * pc * pc * pc * pc * pv * 5 horas con los pre-commits * feat: improving orders to count (#5) * feat: improving orders to count * feat: integrating all sensors under a device (#6) * feat: integrating all sensors under a device * feat: adding sensors to a devide * feat: Adding affiliate influencer and last order sensors (#7) * feat: integrating all sensors under a device * feat: adding sensors to a devide * feat: Adding affiliate influencer and last order sensors * feat: optimize reading data (#8) * feat: optimize reading data * ruff fix * feat: Translate to spanish (#9) * feat: Translate to spanish * feat: Translations * Update Coordinator and reorder code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Pre-Commit fixs * Pre-Commit fixs --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Mguel Ángel <[email protected]>
- Loading branch information
1 parent
d8cfa16
commit 5fd7fdb
Showing
21 changed files
with
1,899 additions
and
875 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
__pycache__ | ||
.idea | ||
/.run/HASS.run.xml | ||
/node_modules/.cache/ | ||
custom_components/test.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,45 @@ | ||
--- | ||
minimum_pre_commit_version: "3.0.4" | ||
repos: | ||
- repo: local | ||
- repo: https://github.com/astral-sh/ruff-pre-commit | ||
rev: v0.3.2 | ||
hooks: | ||
- id: ruff-check | ||
name: 🐶 Ruff Linter | ||
language: system | ||
types: [python] | ||
entry: poetry run ruff check --fix | ||
require_serial: true | ||
stages: [commit, push, manual] | ||
- id: ruff | ||
args: | ||
- --fix | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- id: ruff-format | ||
name: 🐶 Ruff Formatter | ||
language: system | ||
types: [python] | ||
entry: poetry run ruff format | ||
require_serial: true | ||
stages: [commit, push, manual] | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- repo: "https://github.com/pre-commit/pre-commit-hooks" | ||
rev: "v4.5.0" | ||
hooks: | ||
- id: end-of-file-fixer | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- id: trailing-whitespace | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- id: check-json | ||
name: { Check JSON files | ||
language: system | ||
types: [json] | ||
entry: poetry run check-json | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- id: check-toml | ||
name: ✅ Check TOML files | ||
language: system | ||
types: [toml] | ||
entry: poetry run check-toml | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- id: check-yaml | ||
name: ✅ Check YAML files | ||
language: system | ||
types: [yaml] | ||
entry: poetry run check-yaml | ||
- id: check-merge-conflict | ||
name: 💥 Check for merge conflicts | ||
language: system | ||
types: [text] | ||
entry: poetry run check-merge-conflict | ||
- id: check-symlinks | ||
name: 🔗 Check for broken symlinks | ||
language: system | ||
types: [symlink] | ||
entry: poetry run check-symlinks | ||
- id: end-of-file-fixer | ||
name: ⮐ Fix End of Files | ||
language: system | ||
types: [text] | ||
entry: poetry run end-of-file-fixer | ||
stages: [commit, push, manual] | ||
- id: no-commit-to-branch | ||
name: 🛑 Don't commit to main branch | ||
language: system | ||
entry: poetry run no-commit-to-branch | ||
pass_filenames: false | ||
always_run: true | ||
args: | ||
- --branch=main | ||
- id: poetry | ||
name: 📜 Check pyproject with Poetry | ||
language: system | ||
entry: poetry check | ||
pass_filenames: false | ||
always_run: true | ||
- id: pylint | ||
name: 🌟 Starring code with pylint | ||
language: system | ||
types: [python] | ||
entry: poetry run pylint | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- repo: https://github.com/pre-commit/mirrors-prettier | ||
rev: v2.7.1 | ||
rev: v4.0.0-alpha.8 | ||
hooks: | ||
- id: prettier | ||
name: 🎨 Format using prettier | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- repo: https://github.com/PyCQA/pylint | ||
rev: "v3.0.0" | ||
hooks: | ||
- id: pylint | ||
args: | ||
- --ignore=custom_components/aliexpress_openplatform/iop | ||
- --disable=import-error,too-few-public-methods | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) | ||
- repo: "https://github.com/pre-commit/mirrors-mypy" | ||
rev: "v1.13.0" | ||
hooks: | ||
- id: "mypy" | ||
name: "Check type hints (mypy)" | ||
args: [--ignore-missing-imports] | ||
verbose: true | ||
exclude: ^(node_modules/|custom_components/aliexpress_openplatform/iop/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
# ha-aliexpress-openplatform | ||
|
||
A custom Home Assistant integration to track AliExpress affiliate purchases in real-time | ||
|
||
This component enables Home Assistant users to monitor and display affiliate purchases made through AliExpress, leveraging the AliExpress Affiliate API. The integration provides dynamically updated sensors for tracking purchase details, including product names, quantities, prices, and order statuses. Users can easily visualize affiliate sales data within their Home Assistant dashboards and set up custom notifications or automations based on purchase activity. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
custom_components/aliexpress_openplatform/aliexpress_api_handler.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
"""Module to handle interactions with the AliExpress API.""" | ||
|
||
from __future__ import annotations | ||
|
||
import hashlib | ||
import hmac | ||
import logging | ||
import time | ||
from typing import Any | ||
|
||
import requests # type: ignore[import-untyped] | ||
|
||
from .iop import IopClient, IopRequest | ||
|
||
# Base URL for the AliExpress API | ||
ALIEXPRESS_API_URL = "https://api-sg.aliexpress.com/sync" | ||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
def get_order_list( | ||
app_key: str, | ||
app_secret: str, | ||
query_params: dict[str, Any], | ||
pagination: dict[str, int] | None = None, | ||
) -> dict[str, Any]: | ||
"""Make an API call to `aliexpress.affiliate.order.list` using the AliExpress SDK.""" | ||
client = IopClient(ALIEXPRESS_API_URL, app_key, app_secret) | ||
|
||
request = IopRequest("aliexpress.affiliate.order.list", "POST") | ||
for key, value in query_params.items(): | ||
request.add_api_param(key, value) | ||
for key, value in (pagination or {}).items(): | ||
request.add_api_param(key, str(value)) | ||
|
||
response = client.execute(request) | ||
|
||
if not response: | ||
message = response.message if response else "Unknown error" | ||
code = response.code if response else "Unknown code" | ||
error_message = f"API returned an error: {message} (Code: {code})" | ||
raise ValueError(error_message) | ||
|
||
data = response.body | ||
main_key = next((key for key in data if key.endswith("_response")), None) | ||
if not main_key or "resp_result" not in data[main_key]: | ||
error_message = f"Unexpected API response format: {data}" | ||
raise ValueError(error_message) | ||
|
||
return data[main_key]["resp_result"].get("result", {}) | ||
|
||
|
||
def generate_signature(secret: str, params: dict[str, Any]) -> str: | ||
"""Generate the HMAC-SHA256 signature for API authentication. | ||
Args: | ||
---- | ||
secret (str): API secret key provided by AliExpress. | ||
params (dict): Parameters to be signed. | ||
Returns: | ||
------- | ||
str: HMAC-SHA256 signature in hexadecimal format. | ||
""" | ||
sorted_params = sorted((k, v) for k, v in params.items() if k != "sign") | ||
concatenated_params = "".join(f"{k}{v}" for k, v in sorted_params) | ||
return ( | ||
hmac.new( | ||
secret.encode("utf-8"), | ||
concatenated_params.encode("utf-8"), | ||
hashlib.sha256, | ||
) | ||
.hexdigest() | ||
.upper() | ||
) | ||
|
||
|
||
def get_order_list_http_request( | ||
app_key: str, | ||
app_secret: str, | ||
query_params: dict[str, Any], | ||
pagination: dict[str, int] | None = None, | ||
) -> dict[str, Any]: | ||
"""Make an API call to `aliexpress.affiliate.order.list` to fetch orders. | ||
Args: | ||
---- | ||
app_key (str): API key provided by AliExpress. | ||
app_secret (str): API secret key provided by AliExpress. | ||
query_params (dict): Query parameters for the API request. | ||
pagination (dict | None): Dictionary with `page_no` and `page_size`. | ||
Returns: | ||
------- | ||
dict: Processed data from the API response. | ||
Raises: | ||
------ | ||
ValueError: If the API response has an unexpected format. | ||
requests.RequestException: If an HTTP request error occurs. | ||
""" | ||
pagination = pagination or {"page_no": 1, "page_size": 50} | ||
|
||
# Build the query parameters | ||
params = { | ||
"app_key": app_key, | ||
"timestamp": str(int(time.time() * 1000)), | ||
"sign_method": "hmac-sha256", | ||
"method": "aliexpress.affiliate.order.list", | ||
"time_type": "Payment Completed Time", | ||
**query_params, | ||
**pagination, | ||
} | ||
params["sign"] = generate_signature(app_secret, params) | ||
|
||
try: | ||
response = requests.get(ALIEXPRESS_API_URL, params=params, timeout=10) | ||
response.raise_for_status() | ||
data = response.json() | ||
|
||
# Locate the main key in the response | ||
main_key = next((key for key in data if key.endswith("_response")), None) | ||
if not main_key or "resp_result" not in data[main_key]: | ||
_LOGGER.error("Unexpected API response: %s", data) | ||
raise_format_error("The API response does not contain the expected data.") | ||
else: | ||
# Process results within `resp_result` | ||
return data[main_key]["resp_result"].get("result", {}) | ||
|
||
except requests.RequestException as e: | ||
handle_request_exception(e) | ||
except ValueError as e: | ||
raise_format_error(str(e)) | ||
return {} | ||
|
||
|
||
def raise_format_error(message: str) -> None: | ||
"""Raise a format error with a standard message.""" | ||
raise ValueError(message) | ||
|
||
|
||
def handle_request_exception(e: requests.RequestException) -> None: | ||
"""Raise a request error with additional context.""" | ||
error_message = f"HTTP request error to AliExpress API: {e}" | ||
raise requests.RequestException(error_message) from e |
Oops, something went wrong.