# מדריך לפיתוח אפליקציות ללוח הבקרה של Home Assistant

> **הוראה ל-AI שקורא את הקובץ הזה:** המשתמש העלה לך מדריך זה כדי שתכתוב עבורו "אפליקציה" ללוח הבקרה הביתי שלו.
> **התוצר שלך הוא קובץ HTML אחד, שלם ומוכן להתקנה.** המשתמש גורר אותו אל מסך הבית של הדשבורד והוא מותקן אוטומטית.
> עקוב אחרי המדריך במדויק: מבנה הקובץ, הרשאות, ה-API וההתאמה הגרפית. אל תמציא פונקציות או מחלקות שלא מופיעות כאן.
> כתוב את כל הטקסטים שהמשתמש רואה **בעברית**, ובכיוון **מימין לשמאל**.
> **קטלוג רכיבי העיצוב עם הקוד המדויק של כל רכיב נמצא בסעיף 6. העתק ממנו את המבנה ואל תמציא עיצוב משלך.**
> אם הבקשה לא ברורה (איזה מכשיר? איזו פעולה?), שאל שאלה קצרה אחת לפני הכתיבה. אחרת פשוט כתוב.

**שלושה עקרונות שאסור לעבור עליהם:**
1. **עיצוב: רק מהערכה.** משתמשים אך ורק במחלקות `ha-*` ובמשתני ה-CSS (סעיף 6). אין צבעים קבועים, אין גופן משלך, אין סגנון מומצא. אם חסר רכיב, מרכיבים אותו מהמשתנים (`--sf`, `--ct`, `--r-tile`...), לא ממציאים ערכים.
2. **הרשאות: מינימום הכרחי.** מגדירים בבלוק `ha-app` רק מה שהאפליקציה צריכה.
3. **קובץ אחד שלם.** מתחיל ב-`<!doctype html>` ונסגר ב-`</html>`, בלי קוד חסר.

---

## 1. איך זה עובד

- אפליקציה = **קובץ HTML בודד** (HTML + CSS + JavaScript, הכול בתוכו). אין קבצים נוספים.
- המשתמש גורר את הקובץ לדשבורד ← חלון אישור עם ההרשאות והערות על הקובץ ← "התקנה" ← אייקון במסך הבית ובמגירת האפליקציות, והאפליקציה נפתחת.
- האפליקציה נפתחת **במסך מלא**. הדשבורד מצייר בעצמו שורת כותרת עם כפתור חזרה ושם האפליקציה.
- האפליקציה רצה בתוך **iframe מבודד**. היא לא רואה את הטוקן של Home Assistant ולא את שאר הדשבורד. כל התקשורת עוברת דרך אובייקט אחד, **`HA`** (מוזרק אוטומטית).
- הדשבורד מזריק לאפליקציה **ערכת עיצוב מלאה** (משתני צבע, מדדים ומחלקות CSS), כך שהיא נראית כמו שאר הדשבורד, בבהיר ובכהה.
- **האפליקציה משתלבת בכל המערכת (הקשר דו-כיווני):**
  - הדשבורד נותן לאפליקציה את העיצוב, הנתונים החיים והאחסון.
  - האפליקציה נותנת לדשבורד: **ווידג'ט משלה** (תוכן שהיא מחשבת, והמשתמש בוחר מה יוצג בו), **תזמונים** (מופיעים במסך התזמונים, רצים אוטומטית), **שגרות** (כפתורים במסך הבית ובקיצורים), **ווידג'טים** למסך הבית, **התראות**, וכניסה למגירת האפליקציות, לחיפוש ולאפליקציות האחרונות.
  - כל הפעולות שהאפליקציה מבצעת נרשמות בהיסטוריית הפעולות בשם האפליקציה, והאפליקציות נכללות בגיבוי הדשבורד.
- עדכון: גוררים קובץ עם אותו `id`. (כדאי להעלות את `version`.)

---

## 2. שלד חובה של הקובץ

```html
<!doctype html>
<html lang="he" dir="rtl">
<head>
<meta charset="utf-8">
<title>שם האפליקציה</title>

<!-- בלוק ההגדרות. חובה. id="ha-app" בדיוק, JSON תקין (בלי הערות ובלי פסיק בסוף) -->
<script type="application/json" id="ha-app">
{
  "id": "my.app-name",
  "name": "שם האפליקציה",
  "version": "1.0.0",
  "description": "משפט קצר שמסביר מה האפליקציה עושה",
  "icon": "light",
  "color": "#ffb300",
  "entities": ["light.*"],
  "services": ["light.*"]
}
</script>

<style>
  /* CSS של האפליקציה, רק לפריסה ייחודית. משתמשים במחלקות ha-* ובמשתנים (סעיף 6) */
</style>
</head>
<body>
  <div class="ha-page">
    <!-- התוכן -->
  </div>

<script>
  HA.ready(() => {
    // כאן מתחילים. מכאן HA.states מלא, HA.call עובד וכו'.
  });
</script>
</body>
</html>
```

---

## 3. בלוק ההגדרות (`ha-app`)

| שדה | חובה | הסבר |
|---|---|---|
| `id` | כן | מזהה ייחודי: אותיות אנגליות קטנות, ספרות, נקודה, מקף או קו תחתון. 2–48 תווים. אותו id = עדכון. |
| `name` | כן | שם מתחת לאייקון (עברית, קצר: עד ~14 תווים). |
| `version` | לא | למשל `"1.2.0"`. |
| `description` | לא | עד 160 תווים. מוצג בחלון ההתקנה. |
| `icon` | לא | שם אייקון מהרשימה למטה, **או** path בסגנון קו 24×24. ברירת מחדל `scene`. |
| `color` | לא | צבע האייקון במסך הבית, `#rrggbb`. מומלץ צבע סמנטי מהטבלה בסעיף 6. |
| `entities` | לא | **קריאה:** תבניות של מזהי ישויות שהאפליקציה רואה. בלי זה היא לא רואה שום ישות. |
| `services` | לא | **שליטה:** תבניות של `domain.service` שמותר להפעיל. |
| `ws` | לא | סוגי בקשות מתקדמות ל-HA (`HA.request`), למשל `"history/history_during_period"`. |
| `network` | לא | `true` = גישה לאינטרנט. ברירת מחדל `false`. |
| `schedules` | לא | `true` = מותר ליצור תזמונים (סעיף 7). |
| `routines` | לא | `true` = מותר ליצור שגרות (סעיף 7). |
| `notifications` | לא | `true` = מותר לשלוח התראות (סעיף 7). |
| `widgets` | לא | מערך של עד 6 ווידג'טים למסך הבית (סעיף 7). |

**תבניות (glob):** `*` = כל רצף תווים. `"light.*"`, `"sensor.*temp*"`, `"vacuum.robot"`, `"*"`.

**עקרון הרשאות מינימליות.** המשתמש רואה את כל ההרשאות בחלון ההתקנה. `"*"` ב-`entities`/`services` ו-`network:true` מוצגים כאזהרה, השתמש בהם רק כשבאמת צריך.

**אכיפה:** כל קריאה שחורגת מההרשאות נדחית. גם תזמונים, שגרות וכפתורי ווידג'ט שהאפליקציה יוצרת נבדקים מול ההרשאות, גם בזמן יצירתם וגם בזמן ההרצה. פעולות לפי `area_id` / `device_id` / `label_id` נחסמות אלא אם `entities` הוא `"*"`.

**שמות אייקונים:**
`home light climate cover media_player switch sensor lock play pause next prev search cog ha vac hist bolt pen fwd clock fan scene warn bell door battery dnd chev x zin zout fit expand shrink stop locate wifi wifioff more rooms zone pin center tune trash`

אייקון מותאם: path בסגנון קו, למשל `"M12 3v18M3 12h18"`. למילוי: להתחיל ב-`F:`.

---

## 4. הסביבה: מה מותר ומה חסום

| ❌ לא עובד | ✅ במקום זה |
|---|---|
| `localStorage`, `sessionStorage`, `indexedDB`, cookies | `HA.storage.get / set / remove` |
| `fetch` / XHR / WebSocket (חסום) | `HA.call`, `HA.request`. לאינטרנט: `"network": true` |
| ספריות, פונטים, תמונות מכתובת חיצונית | הכול בתוך הקובץ (SVG מוטבע או `data:`). עם `network:true`: סקריפטים מ-`cdnjs.cloudflare.com` ו-`cdn.jsdelivr.net` בלבד. |
| `<form>` | כפתורים ו-`addEventListener` |
| `window.open`, `top`, `parent` | `HA.close()`, `HA.openEntity()`, `HA.openPage()` |
| קבצים נוספים | קובץ אחד (עד 3MB) |

מותר: `alert/confirm/prompt`, `setTimeout/setInterval`, canvas, SVG, אנימציות CSS, Web Audio. אחסון `HA.storage` עד 256KB (JSON).

**בדיקת קובץ בהתקנה:** הדשבורד סורק את הקובץ ומציג "הערות על הקובץ" על צבעים קבועים, `font-family`, `left/right`, `localStorage`, `fetch` בלי `network`, `<form>`, ו-`HA.ready` חסר. **כתוב קובץ שלא מקבל אף הערה.**

---

## 5. ה-API: אובייקט `HA`

### מצב ושליטה

```js
HA.ready(fn)            // fn() פעם אחת כשהנתונים הגיעו. כל הקוד שלך מתחיל מכאן.
HA.states               // אובייקט חי: { "light.bed": {entity_id, state, attributes, last_changed, last_updated} }
                        //   רק ישויות שתואמות ל-entities.
HA.get("light.bed")     // מצב של ישות אחת, או undefined
HA.list("light.")       // מערך כל הישויות שמתחילות ב"light." (בלי פרמטר: הכול)
HA.onChange((id, newState, oldState) => {...})   // newState = null אם הישות נמחקה
HA.call("light", "turn_on", { entity_id: "light.bed", brightness_pct: 60 })
                        // Promise. נדחה עם Error.message בעברית אם אין הרשאה / אין חיבור / HA החזיר שגיאה.
HA.request("history/history_during_period", {...})  // בקשת WebSocket גולמית, רק סוגים שהוגדרו ב-"ws"
```

### מידע נלווה

```js
HA.name(id)   // שם ידידותי או המזהה
HA.area(id)   // שם החדר בעברית או ""
HA.areas      // { area_id: "שם חדר" }
HA.info       // { id, name, version }
HA.demo       // true במצב הדגמה
HA.connected  // true כשיש חיבור (או הדגמה)
```

### ממשק וניווט

```js
HA.toast("הפעולה בוצעה")      // הודעה קצרה בתחתית המסך
HA.setTitle("כותרת")          // משנה את הכותרת בשורה העליונה
HA.close()                    // סוגר את האפליקציה
HA.openEntity("light.bed")    // סוגר את האפליקציה ופותח את חלון המכשיר המקורי של הדשבורד (נדרשת הרשאת קריאה לישות)
HA.openPage("sched")          // סוגר ופותח עמוד של הדשבורד: home | sched | lights | climate | covers | media | switches | sensors
HA.icon("light", {size: 20})  // מחזיר מחרוזת SVG
HA.theme / HA.onTheme(fn)     // { dark, vars }. בדרך כלל מספיקים משתני ה-CSS
```

### ווידג'ט שהאפליקציה מחשבת בעצמה

```js
HA.mode                                   // "app" (מסך האפליקציה) או "widget" (הקובץ רץ ברקע כדי לחשב ווידג'ט)
HA.widgets.define("id", ctx => "<html>")  // מחזיר HTML לווידג'ט. ראה סעיף 7
HA.widgets.onAction("id", (action, value, ctx) => {...})  // לחיצה על אלמנט עם data-xa
HA.widgets.refresh()                      // מבקש חישוב מחדש
```

### אחסון

```js
HA.storage.get("key", defaultValue)   // סינכרוני
HA.storage.set("key", anyJsonValue)
HA.storage.remove("key")
```
פרטי לאפליקציה, נשמר בדפדפן, נמחק עם הסרת האפליקציה, נכלל בגיבוי.

---

## 6. התאמה גרפית (חובה, בלי חריגות)

המטרה: האפליקציה זהה בעיצוב לדשבורד. **הכול מוזרק אוטומטית, אין צורך להעתיק CSS.**

### כלל ברזל

> מרכיבים כל מסך **רק** מהמחלקות `ha-*` שבטבלאות. מחלקה שלא מופיעה כאן לא קיימת. צבע, רדיוס או ריווח שלא מופיעים כאן לא קיימים. בקובץ ה-`<style>` של האפליקציה מותר רק **פריסה** (grid/flex, רוחבים, מרווחים מתוך `--sp-*`) ועיצוב של רכיב ייחודי שבנוי מהמשתנים.

### משתני צבע (מתחלפים אוטומטית בבהיר/כהה)

| משתנה | שימוש |
|---|---|
| `--bg` | רקע הדף |
| `--sf` | משטח / כרטיס / אריח לא פעיל |
| `--ct` | רקע משני: כפתור רגיל, שדה, אייקון לא פעיל, פס רקע |
| `--pr` / `--onp` | צבע ראשי / טקסט עליו. כפתור ראשי, מתג דלוק, כרטיסיה נבחרת |
| `--pc` / `--onpc` | "פעיל עדין" / טקסט עליו. אריח דלוק, צ'יפ נבחר, כפתור משני |
| `--tx` / `--sub` | טקסט ראשי / משני |
| `--ol` | קווי מתאר ומפרידים |
| `--ok` / `--er` | הצלחה / שגיאה |
| `--acc` | הדגשה לפי הטפט של המשתמש |

**צבעי מכשירים** (לאייקון, פס, `color` של האפליקציה): `--c-light` תאורה, `--c-climate` אקלים, `--c-cover` תריסים, `--c-media` מדיה, `--c-switch` מתגים, `--c-lock` מנעולים, `--c-vacuum` שואב, `--c-scene` סצנות, `--c-sensor` חיישנים.

### מדדים (משתנים)

פינות: `--r-tile` 28px (אריחים, כרטיסים) · `--r-sheet` 32px (חלונות, ווידג'טים) · `--r-btn` 24px (כפתורים) · `--r-inner` 20px (רכיבים בתוך כרטיס) · `--r-field` 14px (שדות) · `--r-chip` 10px.
ריווח: `--sp-1` 4 · `--sp-2` 8 · `--sp-3` 12 · `--sp-4` 16 · `--sp-5` 24 (px). צל: `--shadow`.

### מחלקות: טקסט ופריסה

| מחלקה | תיאור |
|---|---|
| `.ha-page` | מכל ראשי (ריווח + רוחב מקסימלי, ממורכז). עוטפים בו את האפליקציה. |
| `.ha-h1` / `.ha-h2` / `.ha-h3` | כותרת ראשית / כותרת קבוצה / כותרת חלון |
| `.ha-sub` / `.ha-label` | טקסט משני קטן / כותרת קטנה מעל שדה או קבוצה |
| `.ha-c-ok` `.ha-c-er` `.ha-c-sub` | צבע טקסט סמנטי |
| `.ha-num` | ספרות ברוחב קבוע |
| `.ha-stack` / `.ha-inline` / `.ha-spread` | אנכי / אופקי עם גלישה / שורה עם רווח בין הקצוות |
| `.ha-grid` | רשת רספונסיבית של אריחים |
| `.ha-card` | כרטיס (28px) |
| `.ha-wg` | משטח בסגנון ווידג'ט (32px) |
| `.ha-divider` | קו מפריד |

### מחלקות: מכשירים ואפליקציות

| מחלקה | תיאור |
|---|---|
| `.ha-tile` | אריח מכשיר. מבנה: `.ha-ti` (עיגול אייקון) + `.ha-tn` (שם) + `.ha-ts` (מצב). `.on` = פעיל, `.un` = לא זמין. |
| `.ha-apps` + `.ha-app` | רשת אייקונים כמו מסך הבית. `.ha-app` מכיל `<i>` (אייקון) + `<span>`. `.on` + `style="--c:var(--c-light)"` לצבע. |

### מחלקות: פקדים

| מחלקה | תיאור |
|---|---|
| `.ha-btn` | כפתור (48px). `.pri` ראשי, `.tonal` משני, `.block` רוחב מלא. |
| `.ha-big` | כפתור גדול (56px) לפעולה מרכזית בחלון. `.act` = ראשי. |
| `.ha-btnrow` | שורת כפתורים שווים (הקודם / השהיה / הבא). |
| `.ha-pill` / `.ha-pills` | כפתור "גלולה" ושורת גלילה שלהן. |
| `.ha-iconbtn` | כפתור עגול עם אייקון (44px). |
| `.ha-more` / `.ha-add` | "אפשרויות נוספות" / "+ הוספה" (רוחב מלא). |
| `.ha-fab` | כפתור צף. |
| `.ha-chip` / `.ha-chips` | מסנן (`.act` = נבחר) ושורת מסננים. |
| `.ha-seg` | בורר מקטעים: `<button>` לכל מקטע, הנבחר `.act`. |
| `.ha-tabs` | כרטיסיות עם קו תחתון: `<button>`, הנבחרת `.act`. |
| `.ha-switch` | `<input type="checkbox" class="ha-switch">` |
| `.ha-sl` + `.ha-slider` | מחוון עם תווית וערך: `<label class="ha-sl"><div class="ha-slhd"><span>בהירות</span><b>60%</b></div><input type="range" class="ha-slider"></label>` |
| `.ha-field` + `.ha-input` / `.ha-select` / `.ha-time` | שדה עם תווית: `<label class="ha-field">שם<input class="ha-input"></label>` |
| `.ha-stepper` | בקרת + / − עם מספר גדול: `<button>−</button><b>24°</b><button>+</button>` |

### מחלקות: תצוגת נתונים

| מחלקה | תיאור |
|---|---|
| `.ha-row` | שורת רשימה: אייקון / `.grow` / פקד, עם קו מפריד |
| `.ha-rule` | שורת תזמון: `.ha-time` (שעה גדולה) + `.grow` + מתג |
| `.ha-bignum` | מספר גדול, עם `<small>` ליחידה |
| `.ha-bars` + `.ha-bar` | פסי התקדמות: `<div class="ha-bar"><div><span>שם</span><b>82%</b></div><i style="--w:82%"></i></div>` (אפשר `--c:var(--c-vacuum)`) |
| `.ha-stats` | רשת נתונים: `<div><b>840</b><span>תווית</span></div>` |
| `.ha-banner` | פס הודעה עם כפתור |
| `.ha-dot` (`.ok` / `.bad`) | נקודת סטטוס |
| `.ha-badge` | תגית |
| `.ha-empty` | "אין מה להציג" |
| `.ha-scrim` + `.ha-sheet` | חלון קופץ. `.ha-scrim` מקבל `.show`. בתוכו `.ha-h3`, `.ha-sub`, `.ha-big`, ו-`.ha-foot` (שורת "סגירה"). |

### כללי עיצוב

1. **RTL תמיד.** תכונות לוגיות (`margin-inline-start`, `padding-inline`, `inset-inline-end`, `text-align:start`), לא left/right. חיצי ניווט מתהפכים.
2. **אל תצייר כפתור חזרה או כותרת עם שם האפליקציה.** הדשבורד מציג אותם. כותרת פנימית מותרת כשהיא מוסיפה מידע ("תאורה" + סיכום).
3. **שום כפתורים כפולים.** כל פעולה מופיעה פעם אחת במסך.
4. **אייקונים:** רק `HA.icon(...)`. בלי אימוג'י כאייקון ממשק.
5. **מגע:** כל אלמנט לחיץ בגובה ≥44px (הרכיבים בערכה כבר כאלה).
6. **צבע = משמעות.** פעיל = `--pc`/`--pr`. כבוי = `--ct`/`--sub`. שגיאה = `--er`. לא צבעים כקישוט.
7. **ריווח:** רק ערכי `--sp-*`. קבוצות עם `.ha-h2`, לא הכול בבת אחת.
8. **תנועה מינימלית:** מעברי `.15s–.25s`. בלי אנימציות מתמשכות.
9. **גופן וצבעי טקסט:** לא מגדירים. הערכה מגדירה.
10. **עברית תמציתית וישירה.** תרגם סטטוסים של HA לעברית ("מנקה", לא "cleaning"). יחידות ליד המספר.
11. **אותו מבנה בכל האפליקציות:** כותרת (`.ha-h1` + `.ha-sub`) ← מסננים (`.ha-chips`/`.ha-seg`) ← תוכן (`.ha-grid` / `.ha-card` עם `.ha-row`) ← פעולות משניות.

### מתכוני עמודים (התחל מאחד מהם)

**א. לוח בקרה (רשת מכשירים):**
```html
<div class="ha-page">
  <div class="ha-spread"><div><h1 class="ha-h1">תאורה</h1><div class="ha-sub" id="sum"></div></div>
    <button class="ha-btn" id="allOff">כיבוי הכול</button></div>
  <div class="ha-chips" style="margin-top:16px"><button class="ha-chip act">הכול</button><button class="ha-chip">סלון</button></div>
  <div class="ha-grid" id="grid"></div>
  <div class="ha-empty" id="empty" hidden>לא נמצאו מכשירים</div>
</div>
```

**ב. עמוד פרטים עם כרטיסיות:**
```html
<div class="ha-page">
  <div class="ha-card"><div class="ha-label" style="margin-top:0">מזגן סלון</div><div class="ha-stepper"><button>−</button><b>24°</b><button>+</button></div>
    <div class="ha-seg"><button class="act">קירור</button><button>חימום</button></div></div>
  <div class="ha-tabs"><button class="act">שליטה</button><button>הגדרות</button></div>
  <div class="ha-card"><div class="ha-row">…</div><div class="ha-row">…</div></div>
</div>
```

**ג. רשימה / הגדרות עם הוספה:**
```html
<div class="ha-page">
  <h1 class="ha-h1">תזמונים</h1>
  <div style="margin-top:16px"><div class="ha-rule"><div class="ha-time">23:30</div><div class="grow"><b>כיבוי תאורה</b><div class="ha-sub">כל יום</div></div><input type="checkbox" class="ha-switch" checked></div></div>
  <button class="ha-add">+ הוספה</button>
</div>
```


### קטלוג רכיבים: קוד להעתקה

כל רכיב מופיע כאן עם המבנה המדויק שלו. **העתק את המבנה כמו שהוא** ושנה רק טקסטים, ערכים ומזהים. `${HA.icon('light')}` הוא מקום לאייקון: כותבים אותו בתוך מחרוזת תבנית (template literal) ב-JS, או מכניסים את ה-SVG ש-`HA.icon(...)` מחזיר. מצב נבחר (`.act`) מעבירים בין כפתורים ב-JS (`classList.toggle("act", btn === selected)`).

**כותרות וטקסט**
```html
<h1 class="ha-h1">כותרת ראשית</h1>
<h2 class="ha-h2">כותרת קבוצה</h2>
<div class="ha-label">כותרת קטנה של שדה</div>
<div class="ha-sub">טקסט משני</div>
<span class="ha-c-ok">הצלחה</span> <span class="ha-c-er">שגיאה</span> <span class="ha-num">12:30</span>
```

**אריחי מכשירים (.on = פעיל)**
```html
<div class="ha-grid">
  <button class="ha-tile on"><div class="ha-ti">${HA.icon('light')}</div><div class="ha-tn">תאורת סלון</div><div class="ha-ts">70%</div></button>
  <button class="ha-tile"><div class="ha-ti">${HA.icon('light')}</div><div class="ha-tn">מנורת קריאה</div><div class="ha-ts">כבוי</div></button>
  <button class="ha-tile un"><div class="ha-ti">${HA.icon('lock')}</div><div class="ha-tn">דלת כניסה</div><div class="ha-ts">לא זמין</div></button>
</div>
```

**אייקוני אפליקציה (כמו במסך הבית)**
```html
<div class="ha-apps">
  <button class="ha-app on" style="--c:var(--c-light)"><i>${HA.icon('light')}</i><span>תאורה</span></button>
  <button class="ha-app on" style="--c:var(--c-climate)"><i>${HA.icon('climate')}</i><span>אקלים</span></button>
  <button class="ha-app"><i>${HA.icon('sensor')}</i><span>חיישנים</span></button>
</div>
```

**כפתורים**
```html
<div class="ha-inline">
  <button class="ha-btn">רגיל</button>
  <button class="ha-btn pri">ראשי</button>
  <button class="ha-btn tonal">משני</button>
  <button class="ha-btn" disabled>מושבת</button>
  <button class="ha-iconbtn" aria-label="הגדרות">${HA.icon('cog')}</button>
</div>
<button class="ha-big act" style="margin-top:12px">פעולה גדולה</button>
<button class="ha-big">פעולה גדולה משנית</button>
<div class="ha-btnrow"><button>הקודם</button><button>השהיה</button><button>הבא</button></div>
<div class="ha-pills"><button class="ha-pill">${HA.icon('play', {size: 20})}<span>הפעלה</span></button><button class="ha-pill"><span>כיבוי הכול</span></button></div>
<button class="ha-more">אפשרויות נוספות</button>
<button class="ha-add">+ הוספה</button>
```

**צ׳יפים, מקטעים וטאבים (.act = נבחר)**
```html
<div class="ha-chips"><button class="ha-chip act">הכול</button><button class="ha-chip">סלון</button><button class="ha-chip">מטבח</button></div>
<div class="ha-seg"><button class="act">קירור</button><button>חימום</button><button>אוטומטי</button></div>
<div class="ha-tabs"><button class="act">ניקוי</button><button>הגדרות</button><button>היסטוריה</button></div>
```

**שורות ורשימות**
```html
<div class="ha-card">
  <div class="ha-row">${HA.icon('light')}<div class="grow"><div>תאורת סלון</div><div class="ha-sub">סלון</div></div><b class="ha-num">70%</b></div>
  <div class="ha-row">${HA.icon('climate')}<div class="grow"><div>מזגן</div><div class="ha-sub">חדר שינה</div></div><input type="checkbox" class="ha-switch" checked aria-label="מזגן"></div>
</div>
<div class="ha-rule" style="margin-top:12px"><div class="ha-time">07:30</div><div class="grow"><b>פתיחת תריסים</b><div class="ha-sub">כל יום</div></div><input type="checkbox" class="ha-switch" checked aria-label="תזמון"></div>
```

**מתגים, מחווני טווח ושדות**
```html
<label class="ha-sl"><div class="ha-slhd"><span>בהירות</span><b>60%</b></div><input type="range" class="ha-slider" min="1" max="100" value="60"></label>
<label class="ha-field">שם<input class="ha-input" type="text" placeholder="הקלידו כאן"></label>
<label class="ha-field">חדר<select class="ha-select"><option>סלון</option><option>מטבח</option></select></label>
<label class="ha-field">שעה<input class="ha-time" type="time" value="07:30"></label>
```

**מספרים גדולים ובקרת טמפרטורה**
```html
<div class="ha-stepper"><button aria-label="הורדה">−</button><b>24°</b><button aria-label="העלאה">+</button></div>
<div class="ha-bignum">23<small> °C</small></div>
```

**פסי התקדמות וסטטיסטיקות**
```html
<div class="ha-bars">
  <div class="ha-bar"><div><span>מברשת ראשית</span><b>82%</b></div><i style="--w:82%;--c:var(--c-vacuum)"></i></div>
  <div class="ha-bar"><div><span>מסנן</span><b>41%</b></div><i style="--w:41%"></i></div>
</div>
<div class="ha-stats"><div><b>840</b><span>מ״ר סה״כ</span></div><div><b>61</b><span>ניקיונות</span></div><div><b>90</b><span>שעות</span></div></div>
```

**הודעות ומצבים**
```html
<div class="ha-banner"><span>מוצג מידע לדוגמה.</span><button>חיבור</button></div>
<span class="ha-dot ok"></span>מחובר <span class="ha-dot bad"></span>אין חיבור <span class="ha-badge">חדש</span>
<div class="ha-empty">לא נמצאו מכשירים</div>
```

**כרטיס וווידג׳ט**
```html
<div class="ha-card"><div class="ha-label" style="margin-top:0">כרטיס</div>תוכן בתוך כרטיס (פינות 28px)</div>
<div class="ha-wg" style="margin-top:12px"><div class="ha-label" style="margin-top:0">ווידג׳ט</div><div class="ha-bignum">24.5<small> °C</small></div></div>
```

**חלון קופץ (פותחים ב-`classList.add("show")` על `.ha-scrim`, סוגרים ב-`remove("show")`)**
```html
<div class="ha-scrim" id="m"><div class="ha-sheet">
  <h3 class="ha-h3">כותרת החלון</h3><div class="ha-sub">תת כותרת</div>
  <button class="ha-big act">אישור</button>
  <div class="ha-foot"><span></span><button>סגירה</button></div>
</div></div>
```

---

## 7. שילוב עם כל המערכת

כל אחד מאלה דורש הרשאה מפורשת בבלוק `ha-app`. הפעולות שיוצרים (`actions`) הן רשימה של `{ "service": "domain.service", "data": {...} }`, ו**חייבות להתאים ל-`services` ול-`entities`**, אחרת היצירה נדחית.

### תזמונים (`"schedules": true`)

מופיעים במסך התזמונים של הדשבורד, ב"התזמון הבא", ורצים אוטומטית בשעה גם כשהאפליקציה סגורה (בזמן שהדשבורד פתוח).

```js
await HA.schedules.save({
  id: "night-off",              // מזהה אצלך (אנגלית). אותו id = עדכון
  name: "כיבוי תאורה בלילה",
  time: "23:30",                // HH:MM
  days: [0,1,2,3,4,5,6],        // 0 = ראשון. ברירת מחדל: כל יום
  on: true,                     // פעיל
  actions: [{ service: "light.turn_off", data: { entity_id: ["light.a", "light.b"] } }]
});
const list = await HA.schedules.list();   // [{id, name, time, days, on, actions}]
await HA.schedules.remove("night-off");
```
עד 20 תזמונים לאפליקציה. המשתמש יכול להפעיל, לכבות או למחוק אותם מהדשבורד.

### שגרות (`"routines": true`)

כפתור במסך הבית (כמו שאר השגרות), נכלל ב"קיצורים" ובפעולות מהירות.

```js
await HA.routines.save({
  id: "all-off", name: "כיבוי כל האורות", icon: "light",   // icon מהרשימה בסעיף 3
  color: "#ffb300",                                         // אופציונלי
  actions: [{ service: "light.turn_off", data: { entity_id: ids } }],
  schedule: { time: "07:00", days: [1,2,3,4,5] }            // אופציונלי: הרצה אוטומטית
});
await HA.routines.list(); await HA.routines.remove("all-off");
```

### התראות (`"notifications": true`)

```js
await HA.notify({ title: "הכביסה הסתיימה", message: "אפשר להוציא", priority: "default" }); // default | low | critical
await HA.notify("טקסט קצר");  // קיצור
```
מופיעות במרכז ההתראות של הדשבורד (בכפוף להעדפות המשתמש). עד 10 לדקה.

### ווידג'טים למסך הבית (`"widgets"`)

הווידג'ט **מצויר על ידי הדשבורד בעיצוב שלו**, מתעדכן חי, ופועל גם כשהאפליקציה סגורה. המשתמש מוסיף אותו דרך "עריכה ← + הוספה". לחיצה על ווידג'ט פותחת את האפליקציה. מגדירים בבלוק `ha-app` (לא בקוד):

```json
"widgets": [
  { "id": "temp",   "type": "stat",     "name": "טמפרטורה", "label": "סלון", "entity": "sensor.t", "decimals": 1, "size": [2, 1] },
  { "id": "climate","type": "entities", "name": "אקלים",    "entities": ["sensor.t", "sensor.h"], "size": [2, 1] },
  { "id": "off",    "type": "buttons",  "name": "כיבוי",    "size": [2, 1],
    "buttons": [ { "label": "כיבוי", "icon": "light", "actions": [ { "service": "light.turn_off", "data": { "entity_id": "light.kit" } } ] } ] },
  { "id": "open",   "type": "launch",   "name": "פתיחה",    "size": [2, 1] }
]
```

| type | מציג | שדות |
|---|---|---|
| `stat` | מספר גדול + שם | `entity` (חובה), `unit`, `decimals`, `label` |
| `entities` | רשימה של עד 8 ישויות עם מצב | `entities` (חובה), `label` |
| `buttons` | עד 6 כפתורי פעולה | `buttons[]` עם `label`, `icon`, `actions` |
| `launch` | אייקון האפליקציה | `label` |
| `app` | **תוכן שהאפליקציה מחשבת בעצמה** (לוח שנה, גרף, סיכום, כל מה שצריך), עם הגדרות שהמשתמש בוחר | `options`, `minSize` (ראה למטה) |

`size`: `[רוחב 1–12, גובה 1–10]` ביחידות של אייקון במסך הבית (הדשבורד מקטין אם אין מקום). עד 6 ווידג'טים לאפליקציה. כל `entity` חייב להתאים ל-`entities`, וכל פעולה ל-`services`.

**מתי לבחור `app`:** כשאף סוג מוכן לא מספיק (לוח חודש, תצוגה משולבת של כמה מקורות, חישוב). **עדיף ווידג'ט `app` אחד שהמשתמש מגדיר מה יוצג בו, על פני הרבה ווידג'טים קטנים.** לפרשנות פשוטה (מספר אחד, רשימה, כפתור) מספיקים הסוגים המוכנים.

### ווידג'ט מסוג `app`

האפליקציה **מחשבת HTML**, והדשבורד מציג אותו בתוך הווידג'ט, בעיצוב ובצבעים שלו, ומעדכן אותו חי. כך הווידג'ט תמיד נראה כמו שאר הדשבורד.

**1. הגדרה בבלוק `ha-app`:**
```json
"widgets": [
  { "id": "main", "type": "app", "name": "לוח שנה", "description": "לוח חודש עם מידע לבחירה",
    "size": [4, 4], "minSize": [2, 2],
    "options": [
      { "id": "show", "type": "multi", "label": "מה להציג",
        "choices": [ { "value": "grid", "label": "לוח החודש" }, { "value": "parsha", "label": "פרשה" } ],
        "default": ["grid", "parsha"] },
      { "id": "greg", "type": "toggle", "label": "תאריך לועזי", "default": true }
    ] }
]
```
`options` (עד 10) הם ההגדרות שהמשתמש בוחר במסך "עריכה ← הגדרות הווידג'ט" (מסך ההגדרות המקורי של הדשבורד, בלי שתכתוב ממשק). סוגים:

| type | המשתמש בוחר | הערך שמגיע אליך ב-`ctx.options` |
|---|---|---|
| `toggle` | הצג / הסתר | `true` / `false` |
| `select` | אפשרות אחת (`choices`) | `value` של הבחירה |
| `multi` | כמה אפשרויות (`choices`) | מערך של `value` (מחרוזות) |
| `entity` | ישות אחת (`domain` אופציונלי לסינון) | מזהה ישות, או `""` (אוטומטי) |

לכל `choice`: `{ "value": "...", "label": "..." }`. `default` הוא ערך ברירת המחדל.

**2. הקוד (בכל מקום בסקריפט, לא חייב בתוך `HA.ready`):**
```js
HA.widgets.define("main", ctx => {
  // ctx.options  = ההגדרות שהמשתמש בחר
  // ctx.size     = { w, h } בגודל הווידג'ט ביחידות אייקון;  ctx.px = { w, h } בפיקסלים
  // ctx.dark     = true במצב כהה
  const t = HA.get("sensor.t");
  return `<div class="ha-label">טמפרטורה</div>
          <div class="ha-bignum">${esc(t ? t.state : "—")}<small>°</small></div>
          <button class="ha-chip" data-xa="refresh">רענון</button>`;
});
HA.widgets.onAction("main", (action, value, ctx) => { /* action = ערך data-xa, value = ערך data-xv */ });
```
הפונקציה נקראת מחדש אוטומטית בכל שינוי ישות, שינוי הגדרה או גודל, ופעם בדקה. בנוסף אפשר לבקש חישוב מחדש עם `HA.widgets.refresh()`.

**3. כשהקובץ רץ בתור ווידג'ט** (`HA.mode === "widget"`), הוא נטען ברקע בלי מסך. בתחילת `HA.ready` כתוב `if (HA.mode === "widget") return;` כדי לא לבנות את מסך האפליקציה בלי צורך.

**4. התאם את התוכן לגודל.** השתמש ב-`ctx.px.h` / `ctx.px.w` כדי להציג פחות כשהווידג'ט קטן (למשל לוח חודש רק מעל 300px גובה, ושורות מידע לפי המקום שנשאר). הווידג'ט לא גולל, ומה שחורג נחתך.

**5. מה מותר ב-HTML של הווידג'ט.** הדשבורד מנקה את ה-HTML. נשארים רק:
- תגיות: `div span p b i u strong em small br hr h1-h4 ul ol li table thead tbody tr th td button` ו-`svg path g circle rect line polyline polygon text`.
- `class`: רק מחלקות `ha-*` מהקטלוג (בלי `ha-page`, `ha-scrim`, `ha-sheet`, `ha-fab`) ועוד `grow`, `on`, `act`, `ok`, `bad`, `un`.
- `style`: רק פריסה ועיצוב בסיסי (`grid-template-columns`, `gap`, `margin*`, `padding*`, `text-align`, `font-size`, `font-weight`, `width`, `height`, `display` grid/flex/block, `color`, `background-color`, `border-radius`, `opacity`, `line-height`, משתני `--c` / `--w`). אין `position`, `url()`, תמונות או קישורים.
- `data-xa` / `data-xv` על כפתורים ואלמנטים: לחיצה עליהם שולחת `action` / `value` ל-`onAction`. **לחיצה על כל מקום אחר בווידג'ט פותחת את האפליקציה.**
- הכול מושתל בעיצוב הדשבורד: השתמש ב-`var(--pr)`, `var(--onp)`, `var(--sub)` וכו' לצבעים, כמו בשאר האפליקציה.

**6. תקשורת בין המסך לווידג'ט:** `HA.storage` משותף בין מסך האפליקציה לווידג'ט (שינוי באחד מתעדכן בשני). מצב זמני (למשל חודש שמוצג בלוח) שמור במשתנה בקוד.

### ניווט לדשבורד

`HA.openEntity(id)` פותח את חלון המכשיר המקורי (כולל אפליקציית השואב). `HA.openPage("sched")` פותח את מסך התזמונים. שימושי ל"לכל ההגדרות המתקדמות...".

---

## 8. נתוני HA: מה יש בישויות

`state` תמיד **מחרוזת**. מספרים צריך להמיר. ערכים נפוצים: `"on"`, `"off"`, `"unavailable"`, `"unknown"`.

| תחום | state | תכונות נפוצות | שירותים (domain.service ← data) |
|---|---|---|---|
| `light` | on / off | `brightness` (0–255), `rgb_color`, `color_temp_kelvin`, `supported_color_modes` | `light.turn_on` ← `brightness_pct` (1–100), `rgb_color`, `color_temp_kelvin`; `turn_off`; `toggle` |
| `switch`, `input_boolean` | on / off | | `turn_on` / `turn_off` / `toggle` |
| `climate` | off / cool / heat / auto / dry / fan_only | `temperature` (יעד), `current_temperature`, `hvac_modes`, `fan_modes`, `target_temp_step` | `set_temperature` ← `temperature`; `set_hvac_mode` ← `hvac_mode`; `set_fan_mode` ← `fan_mode` |
| `cover` | open / closed / opening / closing | `current_position` (0–100) | `open_cover`, `close_cover`, `stop_cover`, `set_cover_position` ← `position` |
| `media_player` | playing / paused / idle / off | `media_title`, `volume_level` (0–1) | `media_play_pause`, `volume_set` ← `volume_level`, `media_next_track` |
| `lock` | locked / unlocked | | `lock`, `unlock` |
| `vacuum` | docked / cleaning / paused / returning / error | `battery_level`, `fan_speed`, `fan_speed_list` | `start`, `pause`, `stop`, `return_to_base`, `locate`, `set_fan_speed` ← `fan_speed` |
| `scene`, `script` | | | `scene.turn_on`, `script.turn_on` |
| `select` | הערך הנבחר | `options` | `select_option` ← `option` |
| `number` | מספר | `min`, `max`, `step` | `set_value` ← `value` |
| `sensor` | הערך | `unit_of_measurement`, `device_class` | אין |
| `binary_sensor` | on / off | `device_class` | אין |
| `weather` | sunny / cloudy / rainy ... | `temperature`, `humidity`, `wind_speed` | אין |

ב-`HA.call` חובה `entity_id` (מחרוזת או מערך) שמתאים ל-`entities`.

---

## 9. דפוסי קוד נכונים

1. **אל תבנה מחדש את ה-DOM בכל שינוי.** `HA.onChange` נקרא תכופות, ובנייה מחדש גורמת למחוון בגרירה "לקפוץ". צור אלמנט לכל ישות פעם אחת (מילון `els[entity_id]`) ועדכן אותו במקום: `el.classList.toggle("on", e.state === "on")`, `el.querySelector(".ha-ts").textContent = ...`. אל תעדכן שדה שנמצא בפוקוס (`document.activeElement`).
2. **מחוונים:** debounce ~150ms לפני `HA.call`.
3. **שגיאות:** `HA.call(...).catch(err => HA.toast(err.message))`.
4. **מצבים:** ישות חסרה (`if (!e)`), `unavailable`, רשימה ריקה (`.ha-empty`), אין חיבור (`HA.connected`).
5. **גילוי דינמי:** מצא מכשירים לפי תחום / `device_class` / חדר (`HA.list`, `HA.area`) ולא לפי מזהים קבועים, כדי שיעבוד בכל בית.
6. **XSS:** טקסט שמגיע מ-HA ונכנס ל-`innerHTML` חייב `esc(...)`; או `textContent`.
7. **שמירת העדפות:** `HA.storage`.
8. **יצירת תזמון/שגרה:** תזמון רק כשהמשתמש ביקש (מתג, כפתור). שגרה קבועה אפשר ליצור בטעינה, ולהתעלם משגיאה (`.catch(() => {})`).
9. **זמן:** `new Date(last_changed)`, `toLocaleTimeString("he-IL", {hour:"2-digit", minute:"2-digit"})`.

---

## 10. תבנית מינימלית שעובדת

```html
<!doctype html>
<html lang="he" dir="rtl">
<head>
<meta charset="utf-8">
<title>טמפרטורות</title>
<script type="application/json" id="ha-app">
{
  "id": "temps.overview",
  "name": "טמפרטורות",
  "version": "1.0.0",
  "description": "כל הטמפרטורות בבית במבט אחד.",
  "icon": "climate",
  "color": "#26c6da",
  "entities": ["sensor.*"]
}
</script>
</head>
<body>
<div class="ha-page">
  <h1 class="ha-h1">טמפרטורות</h1>
  <div class="ha-card" id="list" style="margin-top:16px"></div>
  <div class="ha-empty" id="empty" hidden>לא נמצאו חיישני טמפרטורה</div>
</div>
<script>
const esc = s => String(s).replace(/[&<>"]/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
function render() {
  const rows = HA.list("sensor.").filter(e => e.attributes.device_class === "temperature");
  document.getElementById("empty").hidden = rows.length > 0;
  document.getElementById("list").hidden = rows.length === 0;
  document.getElementById("list").innerHTML = rows.map(e => `
    <div class="ha-row">
      ${HA.icon("climate")}
      <div class="grow">
        <div>${esc(HA.name(e.entity_id))}</div>
        <div class="ha-sub">${esc(HA.area(e.entity_id))}</div>
      </div>
      <b class="ha-num">${esc(e.state)}${esc(e.attributes.unit_of_measurement || "")}</b>
    </div>`).join("");
}
HA.ready(() => {
  render();
  HA.onChange(id => { if (id.startsWith("sensor.")) render(); });
});
</script>
</body>
</html>
```

(בכל התבניות שבמדריך, ה-JavaScript וה-HTML כתובים כך שאפשר להעתיק אותם ישירות.)

---

## 11. בדיקה לפני מסירה

לדשבורד יש **מצב הדגמה** (כשלא מחוברים ל-HA). גוררים אליו את הקובץ ורואים איך הוא נראה ומתנהג. ישויות ההדגמה:

- תאורה: `light.salon_main`, `light.salon_lamp`, `light.bed`, `light.kit`
- אקלים: `climate.salon`, `climate.bed` · תריסים: `cover.salon`, `cover.bed`
- מדיה: `media_player.tv` · מתג: `switch.boiler` · מנעול: `lock.door`
- סצנות: `scene.evening`, `scene.night`
- חיישנים: `sensor.t` (טמפרטורה), `sensor.h` (לחות), `sensor.boiler_p`, `sensor.boiler_e`, `sensor.remote_batt`, `binary_sensor.door`
- מזג אוויר: `weather.home` · שואב: `vacuum.robot` ועוד ישויות `*.robot_*`

במצב הדגמה הפעולות הבסיסיות (הדלקה/כיבוי, טמפרטורה, תריסים, מנעול, שואב) משנות מצב באמת. שגיאת JavaScript באפליקציה מוצגת כהודעה קצרה ונכתבת לקונסול.

**רשימת בדיקה:**
- [ ] בלוק `ha-app` תקין, `id` ייחודי, `name` בעברית, הרשאות מינימליות שמכסות כל קריאה בקוד
- [ ] **אין "הערות על הקובץ" בחלון ההתקנה** (צבעים קבועים, left/right, localStorage, fetch, form)
- [ ] כל המסך בנוי ממחלקות `ha-*` ומשתנים בלבד, מבנה כמו אחד המתכונים בסעיף 6
- [ ] עובד בבהיר ובכהה, RTL תקין, אין כפתור חזרה/כותרת כפולים, אין כפתורים כפולים
- [ ] הכול בתוך `HA.ready`; מצבים: ישות חסרה, `unavailable`, רשימה ריקה, שגיאת פעולה
- [ ] מחוונים עם debounce; ה-DOM לא נבנה מחדש בזמן גרירה
- [ ] תזמונים / שגרות / ווידג'טים / התראות: ההרשאה מוגדרת ב-`ha-app`, והפעולות שבהם מותרות לפי `services` ו-`entities`
- [ ] ווידג'ט `app`: מוגדר ב-`widgets`, ה-HTML שלו משתמש רק במחלקות `ha-*` ובתגיות המותרות, מתאים את עצמו לגודל (`ctx.px`), וב-`HA.mode === "widget"` לא נבנה מסך האפליקציה
- [ ] הקובץ שלם, בלי "..." או קוד חסר

---

## 12. טעויות נפוצות

1. `localStorage` ← קורס. משתמשים ב-`HA.storage`.
2. קוד מחוץ ל-`HA.ready` ← `HA.states` עדיין ריק.
3. שכחו שירות ב-`services` ← "אין הרשאה לשירות". שכחו `entities` ← המסך ריק.
4. `schedules` / `routines` / `notifications` לא הוגדרו ← "האפליקציה לא הוגדרה עם ...".
5. פעולה בתזמון/שגרה/ווידג'ט על ישות או שירות שלא ב-`entities` / `services` ← ההתקנה או היצירה נדחית.
6. `id` עם רווחים, אותיות גדולות או עברית ← ההתקנה נכשלת. JSON לא תקין בבלוק `ha-app` (פסיק בסוף, הערות, גרשיים בודדים) ← ההתקנה נכשלת.
7. הקשחת מזהי ישויות של בית ספציפי ← לא עובד אצל אחרים.
8. צבעים קבועים (`#fff`), `font-family`, `margin-left` ← מופיעה אזהרה, והעיצוב נשבר במצב כהה / RTL.
9. המצאת מחלקות (`.btn-primary`, `.card-title`) ← לא קיימות. רק `ha-*` מהטבלאות.
10. כפתור חזרה או כותרת עם שם האפליקציה בתוך האפליקציה ← כפילות עם הדשבורד.
11. Google Fonts / CDN בלי `network:true` ← נחסם.
11א. ב-HTML של ווידג'ט `app`: `<script>`, `onclick`, `<img>`, `position`, `url()`, או מחלקות שאינן `ha-*` ← מוסרים בשקט והווידג'ט נראה שבור.
11ב. הרבה ווידג'טים קטנים במקום ווידג'ט `app` אחד עם `options` ← מבלבל את המשתמש.
12. תשובה עם הסברים ארוכים במקום הקובץ. **המשתמש צריך קובץ HTML שלם אחד.**

---

## 13. איך להחזיר את התוצר

1. **קובץ HTML אחד** (קובץ להורדה אם אפשר, או בלוק קוד יחיד).
2. שם קובץ מוצע: `<id>.html` (למשל `temps-overview.html`).
3. מתחת לקובץ: משפט אחד על מה שהוא עושה, ורשימה קצרה של ההרשאות שביקשת ולמה.
4. תזכורת: "גררו את הקובץ אל מסך הבית של הדשבורד ואשרו את ההתקנה".
