v1.2.0: HACS-Kompatibilität, App-Icon, bereinigte Konfiguration
- manifest.json: codeowners, documentation, issue_tracker, mdi:matrix icon - hacs.json: homeassistant-Feld entfernt (gehört in manifest.json) - brand/: icon.png, icon@2x.png, dark_icon.png, dark_icon@2x.png hinzugefügt - icon/icon.svg: Matrix-Chat SVG-Icon hinzugefügt - generate_icons.py: PNG-Generator für brand/-Verzeichnis Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
Generate icon PNGs for the matrix_messenger integration.
|
||||
Run once: python generate_icons.py
|
||||
Requires: pip install Pillow
|
||||
Output: icon.png, icon@2x.png, dark_icon.png, dark_icon@2x.png
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw
|
||||
except ImportError:
|
||||
raise SystemExit("Pillow not found. Install it with: pip install Pillow")
|
||||
|
||||
|
||||
NAVY = (26, 26, 46, 255) # #1A1A2E (background)
|
||||
PURPLE = (91, 45, 142, 255) # #5B2D8E (speech bubble)
|
||||
TEAL = ( 3, 218, 198, 255) # #03DAC6 (badge)
|
||||
WHITE = (255, 255, 255, 255)
|
||||
TRANSPARENT = (0, 0, 0, 0)
|
||||
|
||||
DARK_BG = (18, 18, 18, 255) # near-black for dark-mode variant
|
||||
|
||||
|
||||
def draw_icon(size: int, dark: bool) -> Image.Image:
|
||||
s = size / 256
|
||||
|
||||
img = Image.new("RGBA", (size, size), TRANSPARENT)
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
bg = DARK_BG if dark else NAVY
|
||||
|
||||
# ── Outer rounded background ────────────────────────────────────────────
|
||||
d.rounded_rectangle([0, 0, size - 1, size - 1], radius=int(48 * s), fill=bg)
|
||||
|
||||
# ── Speech bubble ───────────────────────────────────────────────────────
|
||||
# Body (rounded rect, top + sides)
|
||||
bx0, by0 = int(36 * s), int(36 * s)
|
||||
bx1, by1 = int(220 * s), int(168 * s)
|
||||
d.rounded_rectangle([bx0, by0, bx1, by1], radius=int(16 * s), fill=PURPLE)
|
||||
|
||||
# Tail triangle pointing down-center
|
||||
cx = int(128 * s)
|
||||
ty = int(168 * s)
|
||||
tw = int(28 * s)
|
||||
th = int(34 * s)
|
||||
d.polygon(
|
||||
[(cx - tw, ty), (cx + tw, ty), (cx, ty + th)],
|
||||
fill=PURPLE,
|
||||
)
|
||||
|
||||
# ── Left bracket [ ──────────────────────────────────────────────────────
|
||||
lx = int(60 * s)
|
||||
bw = int(11 * s)
|
||||
bh = int(60 * s)
|
||||
cap = int(26 * s)
|
||||
cr = int(3 * s)
|
||||
by_top = int(78 * s)
|
||||
by_bot = int(127 * s)
|
||||
|
||||
d.rounded_rectangle([lx, by_top, lx + bw, by_top + bh], radius=cr, fill=WHITE) # vertical
|
||||
d.rounded_rectangle([lx, by_top, lx + cap, by_top + bw], radius=cr, fill=WHITE) # top cap
|
||||
d.rounded_rectangle([lx, by_bot, lx + cap, by_bot + bw], radius=cr, fill=WHITE) # bottom cap
|
||||
|
||||
# ── Right bracket ] ─────────────────────────────────────────────────────
|
||||
rx = int(185 * s)
|
||||
d.rounded_rectangle([rx, by_top, rx + bw, by_top + bh], radius=cr, fill=WHITE)
|
||||
d.rounded_rectangle([rx - cap + bw, by_top, rx + bw, by_top + bw], radius=cr, fill=WHITE)
|
||||
d.rounded_rectangle([rx - cap + bw, by_bot, rx + bw, by_bot + bw], radius=cr, fill=WHITE)
|
||||
|
||||
# ── M letter (polyline: left-up, peak, right-up, right-down) ────────────
|
||||
sw = int(12 * s)
|
||||
pts = [
|
||||
(int(97 * s), int(132 * s)),
|
||||
(int(97 * s), int(84 * s)),
|
||||
(int(128 * s), int(114 * s)),
|
||||
(int(159 * s), int(84 * s)),
|
||||
(int(159 * s), int(132 * s)),
|
||||
]
|
||||
for i in range(len(pts) - 1):
|
||||
x0, y0 = pts[i]
|
||||
x1, y1 = pts[i + 1]
|
||||
d.line([x0, y0, x1, y1], fill=WHITE, width=sw)
|
||||
# round caps
|
||||
for px, py in pts:
|
||||
r = sw // 2
|
||||
d.ellipse([px - r, py - r, px + r, py + r], fill=WHITE)
|
||||
|
||||
# ── Teal badge with three dots ───────────────────────────────────────────
|
||||
bc = int(196 * s)
|
||||
br = int(36 * s)
|
||||
d.ellipse([bc - br, bc - br, bc + br, bc + br], fill=TEAL)
|
||||
|
||||
dot_r = int(7 * s)
|
||||
for dx in (-12, 0, 12):
|
||||
cx2 = bc + int(dx * s)
|
||||
d.ellipse([cx2 - dot_r, bc - dot_r, cx2 + dot_r, bc + dot_r], fill=bg)
|
||||
|
||||
return img
|
||||
|
||||
|
||||
HERE = Path(__file__).parent / "brand"
|
||||
HERE.mkdir(exist_ok=True)
|
||||
|
||||
variants = [
|
||||
("icon.png", 256, False),
|
||||
("icon@2x.png", 512, False),
|
||||
("dark_icon.png", 256, True),
|
||||
("dark_icon@2x.png", 512, True),
|
||||
]
|
||||
|
||||
for filename, size, dark in variants:
|
||||
path = HERE / filename
|
||||
draw_icon(size, dark).save(path, "PNG")
|
||||
print(f" created brand/{path.name} ({size}×{size})")
|
||||
|
||||
print("\nDone. Restart Home Assistant to pick up the new icons.")
|
||||
Reference in New Issue
Block a user