hassl 0.3.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {hassl-0.3.0/hassl.egg-info → hassl-0.3.1}/PKG-INFO +76 -6
  2. {hassl-0.3.0 → hassl-0.3.1}/README.md +75 -5
  3. hassl-0.3.1/hassl/__init__.py +1 -0
  4. hassl-0.3.1/hassl/ast/nodes.py +86 -0
  5. {hassl-0.3.0 → hassl-0.3.1}/hassl/codegen/package.py +353 -13
  6. {hassl-0.3.0 → hassl-0.3.1}/hassl/codegen/rules_min.py +50 -13
  7. {hassl-0.3.0 → hassl-0.3.1}/hassl/parser/hassl.lark +48 -2
  8. hassl-0.3.1/hassl/parser/transform.py +696 -0
  9. {hassl-0.3.0 → hassl-0.3.1}/hassl/semantics/analyzer.py +129 -13
  10. {hassl-0.3.0 → hassl-0.3.1/hassl.egg-info}/PKG-INFO +76 -6
  11. {hassl-0.3.0 → hassl-0.3.1}/hassl.egg-info/SOURCES.txt +2 -1
  12. {hassl-0.3.0 → hassl-0.3.1}/pyproject.toml +1 -1
  13. {hassl-0.3.0 → hassl-0.3.1}/setup.cfg +1 -1
  14. hassl-0.3.1/tests/test_schedule_windows_codegen.py +123 -0
  15. hassl-0.3.0/hassl/__init__.py +0 -1
  16. hassl-0.3.0/hassl/ast/nodes.py +0 -53
  17. hassl-0.3.0/hassl/parser/transform.py +0 -354
  18. {hassl-0.3.0 → hassl-0.3.1}/LICENSE +0 -0
  19. {hassl-0.3.0 → hassl-0.3.1}/MANIFEST.in +0 -0
  20. {hassl-0.3.0 → hassl-0.3.1}/hassl/ast/__init__.py +0 -0
  21. {hassl-0.3.0 → hassl-0.3.1}/hassl/cli.py +0 -0
  22. {hassl-0.3.0 → hassl-0.3.1}/hassl/codegen/__init__.py +0 -0
  23. {hassl-0.3.0 → hassl-0.3.1}/hassl/codegen/generate.py +0 -0
  24. {hassl-0.3.0 → hassl-0.3.1}/hassl/codegen/init.py +0 -0
  25. {hassl-0.3.0 → hassl-0.3.1}/hassl/codegen/yaml_emit.py +0 -0
  26. {hassl-0.3.0 → hassl-0.3.1}/hassl/parser/__init__.py +0 -0
  27. {hassl-0.3.0 → hassl-0.3.1}/hassl/parser/loader.py +0 -0
  28. {hassl-0.3.0 → hassl-0.3.1}/hassl/semantics/__init__.py +0 -0
  29. {hassl-0.3.0 → hassl-0.3.1}/hassl/semantics/domains.py +0 -0
  30. {hassl-0.3.0 → hassl-0.3.1}/hassl.egg-info/dependency_links.txt +0 -0
  31. {hassl-0.3.0 → hassl-0.3.1}/hassl.egg-info/entry_points.txt +0 -0
  32. {hassl-0.3.0 → hassl-0.3.1}/hassl.egg-info/requires.txt +0 -0
  33. {hassl-0.3.0 → hassl-0.3.1}/hassl.egg-info/top_level.txt +0 -0
  34. {hassl-0.3.0 → hassl-0.3.1}/tests/test_codegen_sync_basic.py +0 -0
  35. {hassl-0.3.0 → hassl-0.3.1}/tests/test_golden_ir_sync_shared.py +0 -0
  36. {hassl-0.3.0 → hassl-0.3.1}/tests/test_imports_and_schedules.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hassl
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: HASSL: Home Assistant Simple Scripting Language
5
5
  Home-page: https://github.com/adanowitz/hassl
6
6
  Author: adanowitz
@@ -17,7 +17,7 @@ Dynamic: license-file
17
17
 
18
18
  > **Home Assistant Simple Scripting Language**
19
19
 
20
- ![Version](https://img.shields.io/badge/version-v0.3.0-blue)
20
+ ![Version](https://img.shields.io/badge/version-v0.3.1-blue)
21
21
 
22
22
  HASSL is a human-friendly domain-specific language (DSL) for building **loop-safe**, **deterministic**, and **composable** automations for [Home Assistant](https://www.home-assistant.io/).
23
23
 
@@ -30,11 +30,12 @@ It compiles lightweight `.hassl` scripts into fully functional YAML packages tha
30
30
  - **Readable DSL** → write logic like natural language (`if motion && lux < 50 then light = on`)
31
31
  - **Sync devices** → keep switches, dimmers, and fans perfectly in sync
32
32
  - **Schedules** → declare time-based gates (`enable from 08:00 until 19:00`)
33
+ - **Weekday/weekend/holiday schedules** → full support for Home Assistant’s **Workday integration** (v0.3.1)
33
34
  - **Loop-safe** → context ID tracking prevents feedback loops
34
35
  - **Per-rule enable gates** → `disable rule` or `enable rule` dynamically
35
36
  - **Inline waits** → `wait (!motion for 10m)` works like native HA triggers
36
37
  - **Color temperature in Kelvin** → `light.kelvin = 2700`
37
- - **Modular packages/imports** → split automations across files with public/private exports (v0.3.0)
38
+ - **Modular packages/imports** → split automations across files with public/private exports
38
39
  - **Auto-reload resilience** → schedules re-evaluate automatically on HA restart
39
40
 
40
41
  ---
@@ -147,7 +148,7 @@ Each `.hassl` file compiles into an isolated package — no naming collisions, n
147
148
  | `scripts_<pkg>.yaml` | Writer scripts with context stamping |
148
149
  | `sync_<pkg>_*.yaml` | Sync automations for each property |
149
150
  | `rules_bundled_<pkg>.yaml` | Rule logic automations + schedules |
150
- | `schedules_<pkg>.yaml` | Time/sun-based schedule sensors (v0.3.0) |
151
+ | `schedules_<pkg>.yaml` | Time/sun-based schedule sensors (v0.3.1) |
151
152
 
152
153
  ---
153
154
 
@@ -180,10 +181,79 @@ All schedules are restart-safe:
180
181
 
181
182
  ---
182
183
 
184
+ ## 🗓️ Holiday & Workday Integration (v0.3.1)
185
+
186
+ HASSL now supports `holidays <id>:` schedules tied to Home Assistant’s **Workday** integration.
187
+
188
+ To enable holiday and weekday/weekend-aware schedules:
189
+
190
+ ### 1️⃣ Create two Workday sensors in Home Assistant
191
+
192
+ You must create **two Workday integrations** through the Home Assistant UI.
193
+
194
+ #### Sensor 1 — `binary_sensor.hassl_<id>_workday`
195
+ - **Workdays:** Mon–Fri
196
+ - **Excludes:** `holiday`
197
+ - **Meaning:** ON only on real workdays (Mon–Fri that are not holidays).
198
+
199
+ #### Sensor 2 — `binary_sensor.hassl_<id>_not_holiday`
200
+ - **Workdays:** Mon–Sun
201
+ - **Excludes:** `holiday`
202
+ - **Meaning:** ON every day except official holidays (including weekends).
203
+
204
+ > In both, set your **Country** and optional **Province/Region** as needed for your locale (e.g., `US`, `CA`, `GB`, etc.).
205
+ > After setup, rename the entity IDs to exactly match:
206
+ > - `binary_sensor.hassl_<id>_workday`
207
+ > - `binary_sensor.hassl_<id>_not_holiday`
208
+ > where `<id>` matches the identifier used in your `.hassl` file (e.g., `us_ca`).
209
+
210
+ HASSL derives:
211
+ - `binary_sensor.hassl_holiday_<id>` → ON on holidays (even when they fall on weekends).
212
+
213
+ ### Truth table
214
+
215
+ | Day type | `hassl_<id>_workday` | `hassl_<id>_not_holiday` | `hassl_holiday_<id>` (derived) |
216
+ |------------------------------|-----------------------|---------------------------|---------------------------------|
217
+ | Tue (normal) | on | on | off |
218
+ | Sat (normal weekend) | off | on | off |
219
+ | Mon that’s an official holiday | off | off | on |
220
+ | Sat that’s an official holiday | off | off | on |
221
+
222
+ This distinction lets you build precise schedules like:
223
+ ```hassl
224
+ holidays us_ca:
225
+ country="US", province="CA"
226
+
227
+ schedule master_wake:
228
+ on weekdays 06:00–22:00 except holidays us_ca;
229
+ on weekends 08:00–22:00;
230
+ on holidays us_ca 09:00–22:00;
231
+ ```
232
+
233
+ 🧩 **Note:**
234
+ Both sensors must be created manually in the Home Assistant UI — integrations can’t be defined in YAML.
235
+ Once created, HASSL automatically references them in generated automations.
236
+
237
+ ---
238
+
239
+ ## ⚗️ Experimental: Date & Month Range Schedules
240
+
241
+ HASSL v0.3.1 includes early support for:
242
+
243
+ ```hassl
244
+ on months Jun–Aug 07:00–22:00;
245
+ on dates 12-24..01-02 06:00–20:00;
246
+ ```
247
+
248
+ These may compile successfully but are **not yet validated in production**.
249
+ They’re marked **experimental** and will be verified after template automation support (v0.4 milestone).
250
+
251
+ ---
252
+
183
253
  ## 📚 Documentation
184
254
 
185
- - [Quickstart Guide](./quickstart_v1.4_2025_v0.3.0.md)
186
- - [Language Specification](./hassl_language_spec_v1.4_2025_updated_v0.3.0.md)
255
+ - [Quickstart Guide](./quickstart.md)
256
+ - [Language Specification](./Hassl_spec.md)
187
257
 
188
258
  ---
189
259
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **Home Assistant Simple Scripting Language**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-v0.3.0-blue)
5
+ ![Version](https://img.shields.io/badge/version-v0.3.1-blue)
6
6
 
7
7
  HASSL is a human-friendly domain-specific language (DSL) for building **loop-safe**, **deterministic**, and **composable** automations for [Home Assistant](https://www.home-assistant.io/).
8
8
 
@@ -15,11 +15,12 @@ It compiles lightweight `.hassl` scripts into fully functional YAML packages tha
15
15
  - **Readable DSL** → write logic like natural language (`if motion && lux < 50 then light = on`)
16
16
  - **Sync devices** → keep switches, dimmers, and fans perfectly in sync
17
17
  - **Schedules** → declare time-based gates (`enable from 08:00 until 19:00`)
18
+ - **Weekday/weekend/holiday schedules** → full support for Home Assistant’s **Workday integration** (v0.3.1)
18
19
  - **Loop-safe** → context ID tracking prevents feedback loops
19
20
  - **Per-rule enable gates** → `disable rule` or `enable rule` dynamically
20
21
  - **Inline waits** → `wait (!motion for 10m)` works like native HA triggers
21
22
  - **Color temperature in Kelvin** → `light.kelvin = 2700`
22
- - **Modular packages/imports** → split automations across files with public/private exports (v0.3.0)
23
+ - **Modular packages/imports** → split automations across files with public/private exports
23
24
  - **Auto-reload resilience** → schedules re-evaluate automatically on HA restart
24
25
 
25
26
  ---
@@ -132,7 +133,7 @@ Each `.hassl` file compiles into an isolated package — no naming collisions, n
132
133
  | `scripts_<pkg>.yaml` | Writer scripts with context stamping |
133
134
  | `sync_<pkg>_*.yaml` | Sync automations for each property |
134
135
  | `rules_bundled_<pkg>.yaml` | Rule logic automations + schedules |
135
- | `schedules_<pkg>.yaml` | Time/sun-based schedule sensors (v0.3.0) |
136
+ | `schedules_<pkg>.yaml` | Time/sun-based schedule sensors (v0.3.1) |
136
137
 
137
138
  ---
138
139
 
@@ -165,10 +166,79 @@ All schedules are restart-safe:
165
166
 
166
167
  ---
167
168
 
169
+ ## 🗓️ Holiday & Workday Integration (v0.3.1)
170
+
171
+ HASSL now supports `holidays <id>:` schedules tied to Home Assistant’s **Workday** integration.
172
+
173
+ To enable holiday and weekday/weekend-aware schedules:
174
+
175
+ ### 1️⃣ Create two Workday sensors in Home Assistant
176
+
177
+ You must create **two Workday integrations** through the Home Assistant UI.
178
+
179
+ #### Sensor 1 — `binary_sensor.hassl_<id>_workday`
180
+ - **Workdays:** Mon–Fri
181
+ - **Excludes:** `holiday`
182
+ - **Meaning:** ON only on real workdays (Mon–Fri that are not holidays).
183
+
184
+ #### Sensor 2 — `binary_sensor.hassl_<id>_not_holiday`
185
+ - **Workdays:** Mon–Sun
186
+ - **Excludes:** `holiday`
187
+ - **Meaning:** ON every day except official holidays (including weekends).
188
+
189
+ > In both, set your **Country** and optional **Province/Region** as needed for your locale (e.g., `US`, `CA`, `GB`, etc.).
190
+ > After setup, rename the entity IDs to exactly match:
191
+ > - `binary_sensor.hassl_<id>_workday`
192
+ > - `binary_sensor.hassl_<id>_not_holiday`
193
+ > where `<id>` matches the identifier used in your `.hassl` file (e.g., `us_ca`).
194
+
195
+ HASSL derives:
196
+ - `binary_sensor.hassl_holiday_<id>` → ON on holidays (even when they fall on weekends).
197
+
198
+ ### Truth table
199
+
200
+ | Day type | `hassl_<id>_workday` | `hassl_<id>_not_holiday` | `hassl_holiday_<id>` (derived) |
201
+ |------------------------------|-----------------------|---------------------------|---------------------------------|
202
+ | Tue (normal) | on | on | off |
203
+ | Sat (normal weekend) | off | on | off |
204
+ | Mon that’s an official holiday | off | off | on |
205
+ | Sat that’s an official holiday | off | off | on |
206
+
207
+ This distinction lets you build precise schedules like:
208
+ ```hassl
209
+ holidays us_ca:
210
+ country="US", province="CA"
211
+
212
+ schedule master_wake:
213
+ on weekdays 06:00–22:00 except holidays us_ca;
214
+ on weekends 08:00–22:00;
215
+ on holidays us_ca 09:00–22:00;
216
+ ```
217
+
218
+ 🧩 **Note:**
219
+ Both sensors must be created manually in the Home Assistant UI — integrations can’t be defined in YAML.
220
+ Once created, HASSL automatically references them in generated automations.
221
+
222
+ ---
223
+
224
+ ## ⚗️ Experimental: Date & Month Range Schedules
225
+
226
+ HASSL v0.3.1 includes early support for:
227
+
228
+ ```hassl
229
+ on months Jun–Aug 07:00–22:00;
230
+ on dates 12-24..01-02 06:00–20:00;
231
+ ```
232
+
233
+ These may compile successfully but are **not yet validated in production**.
234
+ They’re marked **experimental** and will be verified after template automation support (v0.4 milestone).
235
+
236
+ ---
237
+
168
238
  ## 📚 Documentation
169
239
 
170
- - [Quickstart Guide](./quickstart_v1.4_2025_v0.3.0.md)
171
- - [Language Specification](./hassl_language_spec_v1.4_2025_updated_v0.3.0.md)
240
+ - [Quickstart Guide](./quickstart.md)
241
+ - [Language Specification](./Hassl_spec.md)
172
242
 
173
243
  ---
174
244
 
@@ -0,0 +1 @@
1
+ __version__ = "0.3.1"
@@ -0,0 +1,86 @@
1
+ from dataclasses import dataclass, asdict, field
2
+ from typing import List, Any, Dict, Optional
3
+
4
+ @dataclass
5
+ class Alias:
6
+ name: str
7
+ entity: str
8
+ private: bool = False
9
+
10
+ @dataclass
11
+ class Sync:
12
+ kind: str
13
+ members: List[str]
14
+ name: str
15
+ invert: List[str] = field(default_factory=list)
16
+
17
+ @dataclass
18
+ class IfClause:
19
+ condition: Dict[str, Any]
20
+ actions: List[Dict[str, Any]]
21
+
22
+ # ---- NEW: Holiday sets & structured schedule windows ----
23
+ @dataclass
24
+ class HolidaySet:
25
+ id: str
26
+ country: str
27
+ province: Optional[str] = None
28
+ add: List[str] = field(default_factory=list) # YYYY-MM-DD
29
+ remove: List[str] = field(default_factory=list)
30
+ workdays: List[str] = field(default_factory=lambda: ["mon","tue","wed","thu","fri"])
31
+ excludes: List[str] = field(default_factory=lambda: ["sat","sun","holiday"])
32
+
33
+ @dataclass
34
+ class PeriodSelector:
35
+ # kind = 'months' | 'dates' | 'range'
36
+ kind: str
37
+ # data:
38
+ # - months: {"list":[Mon,...]} or {"range":[Mon,Mon]}
39
+ # - dates: {"start":"MM-DD","end":"MM-DD"}
40
+ # - range: {"start":"YYYY-MM-DD","end":"YYYY-MM-DD"}
41
+ data: Dict[str, Any]
42
+
43
+ @dataclass
44
+ class ScheduleWindow:
45
+ start: str # "HH:MM"
46
+ end: str # "HH:MM"
47
+ day_selector: str # "weekdays" | "weekends" | "daily"
48
+ period: Optional[PeriodSelector] = None
49
+ holiday_ref: Optional[str] = None # id from HolidaySet (for 'except'/'only')
50
+ holiday_mode: Optional[str] = None # "except" | "only" | None
51
+
52
+ @dataclass
53
+ class Schedule:
54
+ name: str
55
+ # raw clauses as produced by the transformer (legacy form)
56
+ clauses: List[Dict[str, Any]]
57
+ # structured windows for the new 'on ...' syntax (optional)
58
+ windows: List[ScheduleWindow] = field(default_factory=list)
59
+ private: bool = False
60
+
61
+ @dataclass
62
+ class Rule:
63
+ name: str
64
+ # allow schedule dicts
65
+ clauses: List[Any]
66
+
67
+ @dataclass
68
+ class Program:
69
+ statements: List[object]
70
+ package: Optional[str] = None
71
+ # normalized import entries (dicts) from the transformer:
72
+ # {"type":"import","module": "...", "kind": "glob|list|alias", "items":
73
+ #[...], "as": "name"|None}
74
+ imports: List[Dict[str, Any]] = field(default_factory=list)
75
+ def to_dict(self):
76
+ def enc(x):
77
+ if isinstance(x, (Alias, Sync, Rule, IfClause, Schedule,
78
+ HolidaySet, ScheduleWindow, PeriodSelector)):
79
+ d = asdict(x); d["type"] = x.__class__.__name__; return d
80
+ return x
81
+ return {
82
+ "type": "Program",
83
+ "package": self.package,
84
+ "imports": self.imports,
85
+ "statements": [enc(s) for s in self.statements],
86
+ }