1
0
mirror of https://github.com/natekspencer/hacs-oasis_mini.git synced 2025-11-08 05:03:52 -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."""
from __future__ import annotations
import asyncio
import logging
from typing import Any, Awaitable, Final
@@ -9,7 +11,7 @@ from aiohttp import ClientResponseError, ClientSession
import async_timeout
from .const import TRACKS
from .utils import _bit_to_bool
from .utils import _bit_to_bool, decrypt_svg_content
_LOGGER = logging.getLogger(__name__)
@@ -33,7 +35,6 @@ AUTOPLAY_MAP = {
"4": "30 minutes",
}
LED_EFFECTS: Final[dict[str, str]] = {
"0": "Solid",
"1": "Rainbow",
@@ -112,6 +113,7 @@ class OasisMini:
"""Return the drawing progress percent."""
if not (self.track and (svg_content := self.track.get("svg_content"))):
return None
svg_content = decrypt_svg_content(svg_content)
paths = svg_content.split("L")
total = self.track.get("reduced_svg_content", {}).get("1", len(paths))
percent = (100 * self.progress) / total

View File

@@ -1,11 +1,17 @@
"""Oasis Mini utils."""
from __future__ import annotations
import base64
import logging
import math
from xml.etree.ElementTree import Element, SubElement, tostring
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
_LOGGER = logging.getLogger(__name__)
APP_KEY = "5joW8W4Usk4xUXu5bIIgGiHloQmzMZUMgz6NWQnNI04="
BACKGROUND_FILL = ("#CCC9C4", "#28292E")
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")):
try:
if progress is not None:
svg_content = decrypt_svg_content(svg_content)
paths = svg_content.split("L")
total = track.get("reduced_svg_content", {}).get(model_id, len(paths))
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:
_LOGGER.exception(e)
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