Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@
## UI Gallery submissions

- [ ] Uses `/ui-gallery/<card-id>/`.
- [ ] Includes `README.md`, YAML, and `preview.png`.
- [ ] Includes `README.md`, `preview.png`, and `card.yaml` or `dashboard.yaml`.
- [ ] Top-level `ui-gallery/README.md` is updated.
- [ ] Example follows `ui-gallery/reference.txt` format.
- [ ] Required custom cards are documented.
- [ ] Preserves canonical Humidity Intelligence backend entities/helpers.
- [ ] Screenshots and YAML contain no private entity IDs, secrets, addresses, device IDs, tokens, internal URLs, or personal data.
3 changes: 1 addition & 2 deletions .github/workflows/hacs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ on:
- cron: "0 0 * * *"
workflow_dispatch:

permissions:
contents: read
permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
111 changes: 111 additions & 0 deletions .github/workflows/hassfest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Validate with hassfest

on:
push:
pull_request:
schedule:
- cron: "0 0 * * *"

jobs:
validate:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v3"

- name: Stage custom component layout
shell: bash
run: |
set -euo pipefail
mkdir -p custom_components/humidity_intelligence
rsync -a \
__init__.py \
binary_sensor.py \
config_flow.py \
const.py \
manifest.json \
migration.py \
sensor.py \
services.py \
services.yaml \
strings.json \
switch.py \
translations \
automations \
helpers \
sensors \
ui \
custom_components/humidity_intelligence/
rm -f manifest.json

- name: Normalize staged Hassfest metadata
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path

base = Path("custom_components/humidity_intelligence")

manifest_path = base / "manifest.json"
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
ordered_manifest = {
"domain": manifest.pop("domain"),
"name": manifest.pop("name"),
}
for key in sorted(manifest):
ordered_manifest[key] = manifest[key]
manifest_path.write_text(
json.dumps(ordered_manifest, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)

for rel_path in ("strings.json", "translations/en.json"):
path = base / rel_path
data = json.loads(path.read_text(encoding="utf-8"))
for section in ("config", "options"):
root = data.setdefault(section, {})
shared_errors = dict(root.get("error", {}))
for step in root.get("step", {}).values():
if isinstance(step, dict):
shared_errors.update(step.pop("errors", {}) or {})
if shared_errors:
root["error"] = shared_errors

text = json.dumps(data, indent=2, ensure_ascii=False)
text = text.replace(
"humidity_intelligence_cards_<layout>.yaml",
"humidity_intelligence_cards_layout.yaml",
)
path.write_text(text + "\n", encoding="utf-8")
PY

- name: Verify staged Hassfest metadata
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path

base = Path("custom_components/humidity_intelligence")
manifest = json.loads((base / "manifest.json").read_text(encoding="utf-8"))
keys = list(manifest)
expected = ["domain", "name"] + sorted(keys[2:])
if keys != expected:
raise SystemExit(f"Manifest keys are not sorted for Hassfest: {keys}")

for rel_path in ("strings.json", "translations/en.json"):
path = base / rel_path
text = path.read_text(encoding="utf-8")
data = json.loads(text)
if "<layout>" in text:
raise SystemExit(f"{rel_path} still contains <layout>")
for section in ("config", "options"):
for step_name, step in data.get(section, {}).get("step", {}).items():
if isinstance(step, dict) and "errors" in step:
raise SystemExit(f"{rel_path} has nested errors at {section}.{step_name}")
print("Staged Hassfest metadata is normalized.")
PY

- uses: home-assistant/actions/hassfest@master
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project follows a practical changelog format for Home Assistant and HACS us

- Added GitHub community health files, issue forms, validation workflows, and contributor guidance.
- Added CI SAST/security checks, Bandit configuration, broader diagnostics redaction, and cleanup error logging.
- Added Hassfest validation workflow for Home Assistant custom integration checks.
- Updated Hassfest compatibility by staging the root-content HACS layout as `custom_components/humidity_intelligence`, aligning manifest keys, shared flow errors, and Lovelace after-dependency metadata.
- Added pre-Hassfest staged metadata normalization and verification so CI validates the generated custom component tree, not stale root metadata.
- Added a V2 UI Gallery with default mobile, tablet, and legacy mobile examples plus contributor/reference documentation.

## 2.0.3

Expand Down
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ Each Gallery submission should include:
- `card.yaml` or `dashboard.yaml`
- `preview.png`

Gallery examples must preserve canonical Humidity Intelligence backend entities/helpers. Do not publish private entity IDs, secrets, addresses, personal data, or screenshots that reveal sensitive home information.
Use [ui-gallery/CONTRIBUTING.md](ui-gallery/CONTRIBUTING.md) and [ui-gallery/reference.txt](ui-gallery/reference.txt) for the required gallery format.

Gallery examples must preserve canonical Humidity Intelligence backend entities/helpers. Do not publish private entity IDs, secrets, addresses, personal data, or screenshots that reveal sensitive home information. Add or update the matching entry in [ui-gallery/README.md](ui-gallery/README.md) when adding a gallery example.

## Privacy and redaction

Expand Down
39 changes: 23 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

[![Latest Release](https://img.shields.io/github/v/release/senyo888/Humidity-Intelligence?display_name=tag&sort=semver)](https://github.com/senyo888/Humidity-Intelligence/releases)
[![HACS](https://img.shields.io/badge/HACS-Custom%20Integration-orange)](https://hacs.xyz)
[![Home Assistant](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fsenyo888%2FHumidity-Intelligence%2Fmain%2Fmanifest.json&query=%24.homeassistant&prefix=%3E%3D&label=Home%20Assistant&color=blue)](https://www.home-assistant.io/)
[![Home Assistant](https://img.shields.io/badge/Home%20Assistant-2026.4.3%2B-blue)](https://www.home-assistant.io/)
[![License](https://img.shields.io/github/license/senyo888/Humidity-Intelligence)](LICENSE)

<details>
Expand All @@ -21,28 +21,19 @@
</details>

<details>
<summary><strong>V2 UI Gallery (PNG)</strong></summary>
<summary><strong>V2 UI Examples (PNG)</strong></summary>

<br>

<img src="assets/v2_ui_gallery/IMG_5665.png" width="320" alt="V2 UI gallery image 1">

<img src="assets/v2_ui_gallery/IMG_5672.png" width="320" alt="V2 UI gallery image 2">

<img src="assets/v2_ui_gallery/IMG_5673.png" width="320" alt="V2 UI gallery image 3">

<img src="assets/v2_ui_gallery/IMG_5674.png" width="320" alt="V2 UI gallery image 4">

<img src="assets/v2_ui_gallery/IMG_0323.png" width="320" alt="V2 UI gallery image 5">

<img src="assets/v2_ui_gallery/IMG_0324.png" width="320" alt="V2 UI gallery image 6">

<img src="assets/v2_ui_gallery/IMG_5675.png" width="320" alt="V2 UI gallery image 7">

<img src="assets/v2_ui_gallery/ui_v1_mobile.png" width="320" alt="V2 UI gallery image 8">

<img src="assets/v2_ui_gallery/ui_v2_mobile_aq.png" width="320" alt="V2 UI gallery image 9">

<img src="assets/v2_ui_gallery/ui_v2_tablet_zone_2.png" width="320" alt="V2 UI gallery image 10">

</details>
Expand All @@ -60,6 +51,7 @@
- [Migration Guide - v1 to v2](#migration-guide---v1-to-v2)
- [Full Configuration Flow](#full-configuration-flow)
- [Configuration Screenshots (Visual Guide)](#configuration-screenshots-visual-guide)
- [UI Gallery](#ui-gallery)
- [Post-Configuration Workflow](#post-configuration-workflow)
- [How to Use Services](#how-to-use-services)
- [Release Notes](#release-notes)
Expand Down Expand Up @@ -135,7 +127,7 @@ V2 models that instability structurally.

`56%` is not always "high."

Humidity Intelligence v2 evaluates humidity **relative to the active target profile**:
Humidity Intelligence v2.0.3 evaluates humidity **relative to the active target profile**:

- Winter defaults to a lower comfort band than summer
- Spring and autumn use intermediate bands
Expand Down Expand Up @@ -217,7 +209,7 @@ The UI renders.
- dependency status lines now include direct upstream repository links for fast install/verification
- options menu order updated to prioritize setup sequence: Dependencies -> Sensors -> Global Gates
- README dependency guidance expanded and clarified for HACS-first UI setup
- top badges updated for clearer positioning (`Custom Integration`) and auto-synced Home Assistant compatibility from `manifest.json`
- top badges updated for clearer positioning (`Custom Integration`) and Home Assistant compatibility

Upgrade note: **v2.0.3 focuses on dependency clarity, UI onboarding accuracy, and metadata consistency.**
No breaking schema changes are introduced.
Expand Down Expand Up @@ -629,6 +621,20 @@ Example service usage:

---

## UI Gallery

Reusable default UI examples live in [ui-gallery](ui-gallery/README.md).

Included examples:

- [Default V2 Mobile AQ](ui-gallery/default-v2-mobile-aq/README.md)
- [Default V2 Tablet Zone 2](ui-gallery/default-v2-tablet-zone-2/README.md)
- [Default V1 Mobile](ui-gallery/default-v1-mobile/README.md)

For new gallery submissions, follow [ui-gallery/CONTRIBUTING.md](ui-gallery/CONTRIBUTING.md).

---

## Post-Configuration Workflow

When modifying options:
Expand Down Expand Up @@ -796,12 +802,13 @@ Safety guidance:

### v2.0.3

- updated minimum Home Assistant version to `2026.4.3` in `manifest.json`
- bumped integration version to `2.0.3`
- documented minimum Home Assistant version as `2026.4.3`
- dependency status output now includes direct repository links (`card-mod`, `button-card`, `mod-card`, `apexcharts-card`, `HACS`)
- added post-configuration Dependencies options step so dependency checks are accessible after initial setup
- reordered options menu for setup flow clarity (`Dependencies`, then `Sensors`, then `Global Gates`)
- refreshed README dependency section with clearer HACS-first install guidance and acknowledgements
- updated top badges: HACS badge wording now `Custom Integration`, and Home Assistant badge now auto-reads compatibility from `manifest.json`
- updated top badges: HACS badge wording now `Custom Integration`, and Home Assistant compatibility is shown directly

### v2.0.2

Expand All @@ -811,7 +818,7 @@ Safety guidance:
- humidifier telemetry reason expanded with lane scope, trigger condition, measured values vs thresholds, and recovery logic
- runtime debug logs added for active target profile, seasonal adjustments, humidity badge classification, and humidifier trigger/stop events

### v2.0.1
### v2.0.1 fixes

- fixed Fahrenheit telemetry normalization by converting all internal temperature math to Celsius before averages, spreads, deltas, and thresholds
- fixed aggregate behavior so IAQ/AQ averages ignore `unknown`, `unavailable`, and non-numeric states and only return unknown when no valid values exist
Expand Down
3 changes: 3 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv

from .const import DOMAIN
from .services import async_register_services, async_unregister_services
Expand All @@ -15,6 +16,8 @@

_LOGGER = logging.getLogger(__name__)

CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)


async def async_setup(hass: HomeAssistant, config: dict) -> bool:
"""Set up the Humidity Intelligence integration via YAML."""
Expand Down
15 changes: 7 additions & 8 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
{
"domain": "humidity_intelligence",
"name": "Humidity Intelligence",
"icon": "mdi:water-percent",
"version": "2.0.3",
"documentation": "https://github.com/senyo888/humidity-intelligence",
"issue_tracker": "https://github.com/senyo888/humidity-intelligence/issues",
"config_flow": true,
"homeassistant": "2026.4.3",
"requirements": [],
"after_dependencies": ["lovelace"],
"codeowners": ["@senyo888"],
"config_flow": true,
"documentation": "https://github.com/senyo888/Humidity-Intelligence",
"import_executor": true,
"iot_class": "calculated",
"import_executor": true
"issue_tracker": "https://github.com/senyo888/Humidity-Intelligence/issues",
"requirements": [],
"version": "2.0.3"
}
Loading
Loading