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
32 changes: 22 additions & 10 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ NOAA Integration is a Home Assistant custom component that provides NOAA solar a

### Code Validation and Quality
- **ALWAYS validate Python syntax before making changes**:
- `python3 -m py_compile custom_components/noaa_integration/__init__.py custom_components/noaa_integration/sensor.py custom_components/noaa_integration/image.py` -- takes <1 second
- `python3 -m py_compile custom_components/noaa_it_all/__init__.py custom_components/noaa_it_all/sensor.py custom_components/noaa_it_all/image.py` -- takes <1 second
- **ALWAYS run code linting before committing**:
- `flake8 custom_components/noaa_integration/ --max-line-length=120` -- takes <5 seconds
- `flake8 custom_components/noaa_it_all/ --max-line-length=120` -- takes <5 seconds
- Fix all linting issues before proceeding. Common issues: blank line spacing (E302), line length (E501), unused imports (F401)
- **NEVER commit code with linting errors** - the HACS validation will fail

Expand All @@ -32,9 +32,9 @@ For thorough validation of your changes, run all essential checks:
```bash
# Essential validation sequence (takes <30 seconds total)
python3 --version # Verify Python
python3 -m py_compile custom_components/noaa_integration/*.py # Syntax check
flake8 custom_components/noaa_integration/ --max-line-length=120 # Code quality
python3 -c "import json; print('Valid:', json.load(open('custom_components/noaa_integration/manifest.json'))['domain'])" # JSON validation
python3 -m py_compile custom_components/noaa_it_all/*.py # Syntax check
flake8 custom_components/noaa_it_all/ --max-line-length=120 # Code quality
python3 -c "import json; print('Valid:', json.load(open('custom_components/noaa_it_all/manifest.json'))['domain'])" # JSON validation
```
**CRITICAL**: Fix ALL flake8 issues before committing - zero tolerance for linting errors.

Expand Down Expand Up @@ -69,11 +69,23 @@ python3 -c "import json; print('Valid:', json.load(open('custom_components/noaa_

### Key Files and Locations
```
/custom_components/noaa_integration/
├── __init__.py # Component initialization and platform discovery
/custom_components/noaa_it_all/
├── __init__.py # Component setup and platform forwarding
├── manifest.json # Component metadata, dependencies, version
├── sensor.py # Sensor entities: K-index, geomagnetic storm data and interpretations
└── image.py # Image entities: geoelectric field and aurora forecast images
├── config_flow.py # UI config flow (config_flow: true in the manifest)
├── const.py # Constants, API endpoints, defaults
├── coordinator.py # DataUpdateCoordinator: fetches and caches NOAA data
├── parsers.py # Parsing helpers for NOAA API and text-product responses
├── entity_naming.py # Shared entity naming helpers
├── sensor.py # Sensor platform
├── binary_sensor.py # Binary sensor platform
├── image.py # Image platform: geoelectric field and aurora forecast images
├── weather.py # Weather platform
├── strings.json # UI strings for the config flow
├── sensors/ # Per-area sensor definitions (space_weather, surf, alerts,
│ # forecasts, hurricanes, weather_observations, weather_extra)
├── translations/ # en.json
└── brand/ # Brand images served by HA (icon/logo, incl. @2x and dark)
```

### Configuration Files
Expand Down Expand Up @@ -124,7 +136,7 @@ python3 -c "import json; print('Valid:', json.load(open('custom_components/noaa_
4. Users install via HACS in Home Assistant

### Version Management
- Update version in `custom_components/noaa_integration/manifest.json`
- Update version in `custom_components/noaa_it_all/manifest.json`
- Version format: semantic versioning (e.g., "1.0.12")
- HACS uses manifest version for release tracking

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
![Tracked Installs](https://img.shields.io/endpoint?url=https://analytics.home-assistant.io/api/badge_custom_integrations_json/noaa_it_all.json&style=for-the-badge&logo=home-assistant&label=Tracked%20Installs&color=gray)

<p align="center">
<img src="https://github.com/dawg-io/noaa_it_all/blob/main/custom_components/noaa_it_all/brand/icon.png" width="120"><br>
<img src="https://raw.githubusercontent.com/dawg-io/noaa_it_all/main/icon.png" width="120" alt="NOAA It All"><br>
</p>

This Home Assistant integration provides comprehensive NOAA data through sensors and images, with the latest addition of location-specific rip current and surf zone forecasts.
Expand Down
2 changes: 1 addition & 1 deletion hacs.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "NOAA It All",
"content_in_root": false,
"country": "US",
"country": "US",
"render_readme": true,
"homeassistant": "2026.3.0",
"zip_release": false
Expand Down
42 changes: 41 additions & 1 deletion tests/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import os
import re
import struct
import unittest
import zlib
Expand Down Expand Up @@ -218,12 +219,51 @@ def test_corners_are_transparent(self):
)

def test_root_icon_exists(self):
"""icon.png at the repository root is required for HACS store display."""
"""icon.png at the repository root is the README header image source.

It is NOT read by HACS or Home Assistant for any store or UI display.
Those come from custom_components/noaa_it_all/brand/ (inside HA) and from
the home-assistant/brands CDN (the HACS store list).
"""
root_icon = os.path.join(_REPO, "icon.png")
self.assertTrue(os.path.isfile(root_icon), "icon.png missing from repository root")
w, h, _, _, _ = _png_dimensions(root_icon)
self.assertEqual((w, h), (256, 256), "root icon.png must be 256x256")


class TestReadmeImages(unittest.TestCase):
"""Guard against image URLs that render as a broken image.

A github.com/<owner>/<repo>/blob/... URL serves an HTML *page*, not an image,
so it shows as broken on GitHub and on the HACS repository page (hacs.json sets
render_readme, so HACS renders this same markdown).
"""

_FENCE = re.compile(r"^\s*(```|~~~).*?^\s*\1", re.DOTALL | re.MULTILINE)
_HTML_IMG = re.compile(r"<img[^>]*?\ssrc=[\"']([^\"']+)[\"']", re.IGNORECASE)
_MD_IMG = re.compile(r"!\[[^\]]*\]\(\s*([^\s)]+)")

@classmethod
def setUpClass(cls):
with open(os.path.join(_REPO, "README.md"), encoding="utf-8") as f:
body = f.read()
# Drop fenced code blocks first: the Lovelace card examples contain
# <img src="{{ ... }}"> templates that are documentation, not real images.
body = cls._FENCE.sub("", body)
cls.urls = cls._HTML_IMG.findall(body) + cls._MD_IMG.findall(body)

def test_images_found(self):
"""Sanity check that the extraction actually matched something."""
self.assertTrue(self.urls, "no image URLs found in README.md -- extraction is broken")

def test_no_github_blob_image_urls(self):
for url in self.urls:
if "github.com" in url and "/blob/" in url:
self.fail(
f"README.md image URL serves an HTML page, not an image: {url}\n"
"Use https://raw.githubusercontent.com/<owner>/<repo>/<branch>/<path> instead."
)


if __name__ == "__main__":
unittest.main()
Loading