1
0
mirror of https://github.com/natekspencer/hacs-oasis_mini.git synced 2025-11-15 00:23:51 -05:00

Merge pull request #44 from natekspencer/svg-content
Some checks failed
Validate repo / Validate with hassfest (push) Has been cancelled
Validate repo / Validate with HACS (push) Has been cancelled

Fix parsing svg content
This commit is contained in:
Nathan Spencer
2024-12-27 17:14:25 -07:00
committed by GitHub
2 changed files with 36 additions and 2 deletions

View File

@@ -1,5 +1,7 @@
"""Oasis Mini API client.""" """Oasis Mini API client."""
from __future__ import annotations
import asyncio import asyncio
import logging import logging
from typing import Any, Awaitable, Final from typing import Any, Awaitable, Final
@@ -9,7 +11,7 @@ from aiohttp import ClientResponseError, ClientSession
import async_timeout import async_timeout
from .const import TRACKS from .const import TRACKS
from .utils import _bit_to_bool from .utils import _bit_to_bool, decrypt_svg_content
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -33,7 +35,6 @@ AUTOPLAY_MAP = {
"4": "30 minutes", "4": "30 minutes",
} }
LED_EFFECTS: Final[dict[str, str]] = { LED_EFFECTS: Final[dict[str, str]] = {
"0": "Solid", "0": "Solid",
"1": "Rainbow", "1": "Rainbow",
@@ -112,6 +113,7 @@ class OasisMini:
"""Return the drawing progress percent.""" """Return the drawing progress percent."""
if not (self.track and (svg_content := self.track.get("svg_content"))): if not (self.track and (svg_content := self.track.get("svg_content"))):
return None return None
svg_content = decrypt_svg_content(svg_content)
paths = svg_content.split("L") paths = svg_content.split("L")
total = self.track.get("reduced_svg_content", {}).get("1", len(paths)) total = self.track.get("reduced_svg_content", {}).get("1", len(paths))
percent = (100 * self.progress) / total percent = (100 * self.progress) / total

View File

@@ -1,11 +1,17 @@
"""Oasis Mini utils.""" """Oasis Mini utils."""
from __future__ import annotations
import base64
import logging import logging
import math import math
from xml.etree.ElementTree import Element, SubElement, tostring from xml.etree.ElementTree import Element, SubElement, tostring
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
APP_KEY = "5joW8W4Usk4xUXu5bIIgGiHloQmzMZUMgz6NWQnNI04="
BACKGROUND_FILL = ("#CCC9C4", "#28292E") BACKGROUND_FILL = ("#CCC9C4", "#28292E")
COLOR_DARK = ("#28292E", "#F4F5F8") COLOR_DARK = ("#28292E", "#F4F5F8")
@@ -25,6 +31,7 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
if track and (svg_content := track.get("svg_content")): if track and (svg_content := track.get("svg_content")):
try: try:
if progress is not None: if progress is not None:
svg_content = decrypt_svg_content(svg_content)
paths = svg_content.split("L") paths = svg_content.split("L")
total = track.get("reduced_svg_content", {}).get(model_id, len(paths)) total = track.get("reduced_svg_content", {}).get(model_id, len(paths))
percent = min((100 * progress) / total, 100) percent = min((100 * progress) / total, 100)
@@ -137,3 +144,28 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
except Exception as e: except Exception as e:
_LOGGER.exception(e) _LOGGER.exception(e)
return None return None
def decrypt_svg_content(svg_content: dict[str, str]):
"""Decrypt SVG content using AES CBC mode."""
if decrypted := svg_content.get("decrypted"):
return decrypted
# decode base64-encoded data
key = base64.b64decode(APP_KEY)
iv = base64.b64decode(svg_content["iv"])
ciphertext = base64.b64decode(svg_content["content"])
# create the cipher and decrypt the ciphertext
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
decryptor = cipher.decryptor()
decrypted = decryptor.update(ciphertext) + decryptor.finalize()
# remove PKCS7 padding
pad_len = decrypted[-1]
decrypted = decrypted[:-pad_len].decode("utf-8")
# save decrypted data so we don't have to do this each time
svg_content["decrypted"] = decrypted
return decrypted