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
9 changes: 9 additions & 0 deletions .github/codeql/codeql-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: "CodeQL config"

# Exclude the Vite-built JS bundle from analysis. It is generated output —
# the authoritative source is the TypeScript under src/, which CodeQL analyses
# directly. Scanning the bundle produces false positives (e.g. lit-html's
# internal /-->/ template-parser regex flagged as a bad HTML sanitiser) with
# no actionable remediation path.
paths-ignore:
- "custom_components/hass_datapoints/hass-datapoints-cards.js"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @type {import('@commitlint/types').UserConfig} */
module.exports = {
export default {
extends: ["@commitlint/config-conventional"],
rules: {
// Allow both `doc` (historically used here) and `docs` (conventional spec)
Expand All @@ -22,5 +22,7 @@ module.exports = {
],
// Allow any subject casing — sentence case reads better for this project
"subject-case": [0],
// Allow long body lines for detailed commit descriptions
"body-max-line-length": [0],
},
};
11 changes: 9 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ on:
branches: [main]
workflow_dispatch:

# Default to read-only for all jobs. Jobs that need additional permissions
# (e.g. build provenance attestation) declare them explicitly at the job level,
# which fully overrides this block for that job.
permissions:
contents: read

jobs:
# ══════════════════════════════════════════════════════════════════════════════
# Stage 1 — Lint & structural checks (all run in parallel)
Expand Down Expand Up @@ -101,8 +107,8 @@ jobs:
with:
python-version: "3.12"

- name: Install pytest
run: pip install pytest pytest-asyncio --quiet
- name: Install Python dependencies
run: pip install -r requirements_dev.txt --quiet

- name: Run Python tests
run: pytest tests/ -v
Expand Down Expand Up @@ -197,6 +203,7 @@ jobs:
needs: [test-python, test-unit, test-storybook, test-chromatic, hacs]
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
attestations: write
steps:
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/commit-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ jobs:
commitlint:
name: Validate commit messages
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -17,6 +20,6 @@ jobs:
- name: Lint commit messages
uses: wagoid/commitlint-github-action@v6
with:
configFile: .github/commitlint.config.js
configFile: .github/commitlint.config.mjs
failOnWarnings: false
helpURL: https://www.conventionalcommits.org/en/v1.0.0/
6 changes: 6 additions & 0 deletions .npmpackagejsonlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rules": {
"prefer-absolute-version-dependencies": "error",
"prefer-absolute-version-devDependencies": "error"
}
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1000,5 +1000,5 @@ pnpm dev:watch

- CI checks build correctness and integration metadata.
- The built frontend bundle is committed as `custom_components/hass_datapoints/hass-datapoints-cards.js`.
- Pre-commit hooks format staged files, validate frontend types, and rebuild the frontend when needed.
- Pre-push hooks run tests, lint checks, and frontend type validation before pushing.
- Pre-commit hooks format staged files, lint package.json versions, validate frontend types, and rebuild the frontend when needed.
- Pre-push hooks run tests, lint checks, package.json version linting, and frontend type validation before pushing.
33 changes: 28 additions & 5 deletions custom_components/hass_datapoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,27 @@ def _find_automation_id(hass: HomeAssistant, context) -> str | None:

SERVICE_RECORD_SCHEMA = vol.Schema(
{
vol.Required(ATTR_MESSAGE): cv.string,
vol.Optional(ATTR_ANNOTATION): cv.string,
vol.Required(ATTR_MESSAGE): vol.All(
cv.string, vol.Length(max=ws_api._MAX_LEN_MESSAGE)
),
vol.Optional(ATTR_ANNOTATION): vol.All(
cv.string, vol.Length(max=ws_api._MAX_LEN_MESSAGE)
),
vol.Optional(ATTR_ENTITY_IDS): vol.All(cv.ensure_list, [cv.entity_id]),
vol.Optional(ATTR_DEVICE_IDS): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_AREA_IDS): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_LABEL_IDS): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_ICON): cv.string,
vol.Optional(ATTR_COLOR): vol.Any(
vol.Optional(ATTR_ICON): vol.All(
cv.string,
vol.Length(max=ws_api._MAX_LEN_ICON),
vol.Match(ws_api._RE_MDI_ICON),
),
vol.Optional(ATTR_COLOR): vol.Any(
vol.All(
cv.string,
vol.Length(max=ws_api._MAX_LEN_COLOR),
vol.Match(ws_api._RE_HEX_COLOR),
),
vol.All(
[vol.Coerce(int)],
vol.Length(min=3, max=3),
Expand Down Expand Up @@ -142,10 +154,21 @@ async def handle_record(call: ServiceCall) -> None:

automation_id = _find_automation_id(hass, call.context)

# For user-initiated calls, filter entity_ids to those the caller is
# permitted to read. Automation / system calls (no user_id) bypass
# this check so they can tag events with any entity.
entity_ids = call.data.get(ATTR_ENTITY_IDS)
if call.context.user_id:
user = await hass.auth.async_get_user(call.context.user_id)
if user is not None and not user.is_admin and entity_ids:
entity_ids = [
eid for eid in entity_ids if ws_api._can_read_entity(user, eid)
]

event_data = await store.async_record(
message=call.data[ATTR_MESSAGE],
annotation=call.data.get(ATTR_ANNOTATION),
entity_ids=call.data.get(ATTR_ENTITY_IDS),
entity_ids=entity_ids,
device_ids=call.data.get(ATTR_DEVICE_IDS),
area_ids=call.data.get(ATTR_AREA_IDS),
label_ids=call.data.get(ATTR_LABEL_IDS),
Expand Down
Loading
Loading