mirror of
https://github.com/natekspencer/hacs-oasis_mini.git
synced 2025-11-14 16:13: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
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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user