mirror of
https://github.com/home-assistant/core.git
synced 2026-04-16 14:46:15 +02:00
Compare commits
10 Commits
dependabot
...
ariel-pyth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
287c0456a2 | ||
|
|
7dd5b986c0 | ||
|
|
5ecae7066b | ||
|
|
ac9bf9b7cb | ||
|
|
d4a98c3336 | ||
|
|
f0aae350b5 | ||
|
|
69332ed822 | ||
|
|
32db17fab9 | ||
|
|
84e8cff2ea | ||
|
|
cfe390e4f6 |
4
.github/workflows/builder.yml
vendored
4
.github/workflows/builder.yml
vendored
@@ -530,7 +530,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: . # So action will not pull the repository again
|
||||
file: ./script/hassfest/docker/Dockerfile
|
||||
@@ -543,7 +543,7 @@ jobs:
|
||||
- name: Push Docker image
|
||||
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
|
||||
id: push
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: . # So action will not pull the repository again
|
||||
file: ./script/hassfest/docker/Dockerfile
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
- name: Check if integration label was added and extract details
|
||||
id: extract
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
// Debug: Log the event payload
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
- name: Fetch similar issues
|
||||
id: fetch_similar
|
||||
if: steps.extract.outputs.should_continue == 'true'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
INTEGRATION_LABELS: ${{ steps.extract.outputs.integration_labels }}
|
||||
CURRENT_NUMBER: ${{ steps.extract.outputs.current_number }}
|
||||
@@ -285,7 +285,7 @@ jobs:
|
||||
- name: Post duplicate detection results
|
||||
id: post_results
|
||||
if: steps.extract.outputs.should_continue == 'true' && steps.fetch_similar.outputs.has_similar == 'true'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
AI_RESPONSE: ${{ steps.ai_detection.outputs.response }}
|
||||
SIMILAR_ISSUES: ${{ steps.fetch_similar.outputs.similar_issues }}
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
- name: Check issue language
|
||||
id: detect_language
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
|
||||
- name: Process non-English issues
|
||||
if: steps.detect_language.outputs.should_continue == 'true'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
AI_RESPONSE: ${{ steps.ai_language_detection.outputs.response }}
|
||||
ISSUE_NUMBER: ${{ steps.detect_language.outputs.issue_number }}
|
||||
|
||||
4
.github/workflows/restrict-task-creation.yml
vendored
4
.github/workflows/restrict-task-creation.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
|| github.event.issue.type.name == 'Opportunity'
|
||||
steps:
|
||||
- name: Add no-stale label
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.addLabels({
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
if: github.event.issue.type.name == 'Task'
|
||||
steps:
|
||||
- name: Check if user is authorized
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
const issueAuthor = context.payload.issue.user.login;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -249,6 +250,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -269,6 +273,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Carbon monoxide cleared"
|
||||
@@ -279,6 +286,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -290,6 +300,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Carbon monoxide detected"
|
||||
@@ -299,6 +312,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Gas cleared"
|
||||
@@ -308,6 +324,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Gas detected"
|
||||
@@ -327,6 +346,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -348,6 +370,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -369,6 +394,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -390,6 +418,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -411,6 +442,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -432,6 +466,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -453,6 +490,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -474,6 +514,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -485,6 +528,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Smoke cleared"
|
||||
@@ -494,6 +540,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Smoke detected"
|
||||
@@ -513,6 +562,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -534,6 +586,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -555,6 +610,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::air_quality::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
# --- Unit lists for multi-unit pollutants ---
|
||||
|
||||
@@ -163,6 +168,7 @@
|
||||
# Binary sensor detected/cleared trigger fields
|
||||
.trigger_binary_fields: &trigger_binary_fields
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
|
||||
# --- Binary sensor targets ---
|
||||
|
||||
@@ -294,6 +300,7 @@ co_crossed_threshold:
|
||||
target: *target_co_sensor
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -320,6 +327,7 @@ co2_crossed_threshold:
|
||||
target: *target_co2
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -344,6 +352,7 @@ pm1_crossed_threshold:
|
||||
target: *target_pm1
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -368,6 +377,7 @@ pm25_crossed_threshold:
|
||||
target: *target_pm25
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -392,6 +402,7 @@ pm4_crossed_threshold:
|
||||
target: *target_pm4
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -416,6 +427,7 @@ pm10_crossed_threshold:
|
||||
target: *target_pm10
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -442,6 +454,7 @@ ozone_crossed_threshold:
|
||||
target: *target_ozone
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -470,6 +483,7 @@ voc_crossed_threshold:
|
||||
target: *target_voc
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -498,6 +512,7 @@ voc_ratio_crossed_threshold:
|
||||
target: *target_voc_ratio
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -526,6 +541,7 @@ no_crossed_threshold:
|
||||
target: *target_no
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -554,6 +570,7 @@ no2_crossed_threshold:
|
||||
target: *target_no2
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -580,6 +597,7 @@ n2o_crossed_threshold:
|
||||
target: *target_n2o
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -606,6 +624,7 @@ so2_crossed_threshold:
|
||||
target: *target_so2
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_armed": {
|
||||
@@ -234,6 +235,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Alarm armed"
|
||||
@@ -243,6 +247,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Alarm armed away"
|
||||
@@ -252,6 +259,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Alarm armed home"
|
||||
@@ -261,6 +271,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Alarm armed night"
|
||||
@@ -270,6 +283,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Alarm armed vacation"
|
||||
@@ -279,6 +295,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Alarm disarmed"
|
||||
@@ -288,6 +307,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::alarm_control_panel::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Alarm triggered"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
armed: *trigger_common
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_idle": {
|
||||
@@ -160,6 +161,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Satellite became idle"
|
||||
@@ -169,6 +173,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Satellite started listening"
|
||||
@@ -178,6 +185,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Satellite started processing"
|
||||
@@ -187,6 +197,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::assist_satellite::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Satellite started responding"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
idle: *trigger_common
|
||||
listening: *trigger_common
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -87,6 +88,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::battery::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::battery::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::battery::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -98,6 +102,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::battery::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::battery::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Battery low"
|
||||
@@ -107,6 +114,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::battery::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::battery::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Battery not low"
|
||||
@@ -116,6 +126,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::battery::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::battery::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Battery started charging"
|
||||
@@ -125,6 +138,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::battery::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::battery::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Battery stopped charging"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.battery_threshold_entity: &battery_threshold_entity
|
||||
- domain: input_number
|
||||
@@ -42,21 +47,25 @@
|
||||
low:
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
target: *trigger_target_battery
|
||||
|
||||
not_low:
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
target: *trigger_target_battery
|
||||
|
||||
started_charging:
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
target: *trigger_target_charging
|
||||
|
||||
stopped_charging:
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
target: *trigger_target_charging
|
||||
|
||||
level_changed:
|
||||
@@ -74,6 +83,7 @@ level_crossed_threshold:
|
||||
target: *trigger_target_percentage
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -385,6 +386,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
},
|
||||
"hvac_mode": {
|
||||
"description": "The HVAC modes to trigger on.",
|
||||
"name": "Modes"
|
||||
@@ -397,6 +401,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Climate-control device started cooling"
|
||||
@@ -406,6 +413,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Climate-control device started drying"
|
||||
@@ -415,6 +425,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Climate-control device started heating"
|
||||
@@ -434,6 +447,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::climate::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -455,6 +471,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::climate::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -466,6 +485,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Climate-control device turned off"
|
||||
@@ -475,6 +497,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::climate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::climate::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Climate-control device turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.humidity_threshold_entity: &humidity_threshold_entity
|
||||
- domain: input_number
|
||||
@@ -50,6 +55,7 @@ hvac_mode_changed:
|
||||
target: *trigger_climate_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
hvac_mode:
|
||||
context:
|
||||
filter_target: target
|
||||
@@ -76,6 +82,7 @@ target_humidity_crossed_threshold:
|
||||
target: *trigger_climate_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
@@ -101,6 +108,7 @@ target_temperature_crossed_threshold:
|
||||
target: *trigger_climate_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"common": {
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_value": {
|
||||
@@ -96,6 +97,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::counter::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::counter::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Counter reached maximum"
|
||||
@@ -105,6 +109,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::counter::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::counter::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Counter reached minimum"
|
||||
@@ -114,6 +121,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::counter::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::counter::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Counter reset"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
incremented:
|
||||
target:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"awning_is_closed": {
|
||||
@@ -254,6 +255,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Awning closed"
|
||||
@@ -263,6 +267,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Awning opened"
|
||||
@@ -272,6 +279,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Blind closed"
|
||||
@@ -281,6 +291,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Blind opened"
|
||||
@@ -290,6 +303,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Curtain closed"
|
||||
@@ -299,6 +315,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Curtain opened"
|
||||
@@ -308,6 +327,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Shade closed"
|
||||
@@ -317,6 +339,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Shade opened"
|
||||
@@ -326,6 +351,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Shutter closed"
|
||||
@@ -335,6 +363,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::cover::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Shutter opened"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
awning_closed:
|
||||
fields: *trigger_common_fields
|
||||
|
||||
@@ -45,7 +45,7 @@ class DemoImageProcessingFace(ImageProcessingFaceEntity):
|
||||
"""Return minimum confidence for send events."""
|
||||
return 80
|
||||
|
||||
def process_image(self, image: bytes) -> None:
|
||||
async def async_process_image(self, image: bytes) -> None:
|
||||
"""Process image."""
|
||||
demo_data = [
|
||||
FaceInformation(
|
||||
@@ -58,4 +58,4 @@ class DemoImageProcessingFace(ImageProcessingFaceEntity):
|
||||
FaceInformation(confidence=62.53, name="Luna"),
|
||||
]
|
||||
|
||||
self.process_faces(demo_data, 4)
|
||||
self.async_process_faces(demo_data, 4)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_home": {
|
||||
@@ -126,6 +127,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::device_tracker::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::device_tracker::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Entered home"
|
||||
@@ -135,6 +139,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::device_tracker::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::device_tracker::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Left home"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
entered_home: *trigger_common
|
||||
left_home: *trigger_common
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_closed": {
|
||||
@@ -45,6 +46,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::door::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::door::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Door closed"
|
||||
@@ -54,6 +58,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::door::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::door::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Door opened"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
closed:
|
||||
fields: *trigger_common_fields
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["duco"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["python-duco-client==0.3.0"]
|
||||
"requirements": ["python-duco-client==0.3.1"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_off": {
|
||||
@@ -196,6 +197,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::fan::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::fan::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Fan turned off"
|
||||
@@ -205,6 +209,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::fan::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::fan::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Fan turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
turned_on: *trigger_common
|
||||
turned_off: *trigger_common
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_closed": {
|
||||
@@ -45,6 +46,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::garage_door::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::garage_door::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Garage door closed"
|
||||
@@ -54,6 +58,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::garage_door::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::garage_door::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Garage door opened"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
closed:
|
||||
fields: *trigger_common_fields
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_closed": {
|
||||
@@ -45,6 +46,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::gate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::gate::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Gate closed"
|
||||
@@ -54,6 +58,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::gate::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::gate::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Gate opened"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
closed:
|
||||
fields: *trigger_common_fields
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_drying": {
|
||||
@@ -211,6 +212,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::humidifier::common::trigger_for_name%]"
|
||||
},
|
||||
"mode": {
|
||||
"description": "The operation modes to trigger on.",
|
||||
"name": "Mode"
|
||||
@@ -223,6 +227,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::humidifier::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Humidifier started drying"
|
||||
@@ -232,6 +239,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::humidifier::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Humidifier started humidifying"
|
||||
@@ -241,6 +251,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::humidifier::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Humidifier turned off"
|
||||
@@ -250,6 +263,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::humidifier::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Humidifier turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
started_drying: *trigger_common
|
||||
started_humidifying: *trigger_common
|
||||
@@ -23,6 +28,7 @@ mode_changed:
|
||||
target: *trigger_humidifier_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
mode:
|
||||
context:
|
||||
filter_target: target
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -51,6 +52,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::humidity::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::humidity::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::humidity::common::trigger_threshold_name%]"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.humidity_threshold_entity: &humidity_threshold_entity
|
||||
- domain: input_number
|
||||
@@ -47,6 +52,7 @@ crossed_threshold:
|
||||
target: *trigger_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -68,6 +69,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::illuminance::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::illuminance::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Light cleared"
|
||||
@@ -78,6 +82,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::illuminance::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::illuminance::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::illuminance::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -89,6 +96,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::illuminance::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::illuminance::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Light detected"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.illuminance_threshold_entity: &illuminance_threshold_entity
|
||||
- domain: input_number
|
||||
@@ -55,6 +60,7 @@ crossed_threshold:
|
||||
target: *trigger_numerical_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/infrared",
|
||||
"integration_type": "entity",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["infrared-protocols==1.1.0"]
|
||||
"requirements": ["infrared-protocols==1.2.0"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_docked": {
|
||||
@@ -98,6 +99,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lawn mower returned to dock"
|
||||
@@ -107,6 +111,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lawn mower encountered an error"
|
||||
@@ -116,6 +123,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lawn mower paused mowing"
|
||||
@@ -125,6 +135,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lawn mower started mowing"
|
||||
@@ -134,6 +147,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lawn_mower::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lawn mower started returning to dock"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
docked: *trigger_common
|
||||
errored: *trigger_common
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"field_xy_color_name": "XY-color",
|
||||
"section_advanced_fields_name": "Advanced options",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -515,6 +516,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::light::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::light::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::light::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -526,6 +530,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::light::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::light::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Light turned off"
|
||||
@@ -535,6 +542,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::light::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::light::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Light turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.brightness_threshold_entity: &brightness_threshold_entity
|
||||
- domain: input_number
|
||||
@@ -46,6 +51,7 @@ brightness_crossed_threshold:
|
||||
target: *trigger_light_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_jammed": {
|
||||
@@ -146,6 +147,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lock::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lock::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lock jammed"
|
||||
@@ -155,6 +159,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lock::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lock::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lock locked"
|
||||
@@ -164,6 +171,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lock::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lock::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lock opened"
|
||||
@@ -173,6 +183,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::lock::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::lock::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Lock unlocked"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
jammed: *trigger_common
|
||||
locked: *trigger_common
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_not_playing": {
|
||||
@@ -438,6 +439,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::media_player::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::media_player::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Media player paused playing"
|
||||
@@ -447,6 +451,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::media_player::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::media_player::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Media player started playing"
|
||||
@@ -456,6 +463,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::media_player::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::media_player::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Media player stopped playing"
|
||||
@@ -465,6 +475,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::media_player::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::media_player::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Media player turned off"
|
||||
@@ -474,6 +487,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::media_player::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::media_player::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Media player turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
paused_playing: *trigger_common
|
||||
started_playing: *trigger_common
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -68,6 +69,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::moisture::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::moisture::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Moisture cleared"
|
||||
@@ -78,6 +82,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::moisture::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::moisture::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::moisture::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -89,6 +96,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::moisture::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::moisture::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Moisture detected"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.moisture_threshold_entity: &moisture_threshold_entity
|
||||
- domain: input_number
|
||||
@@ -57,6 +62,7 @@ crossed_threshold:
|
||||
target: *trigger_numerical_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_detected": {
|
||||
@@ -45,6 +46,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::occupancy::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::occupancy::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Occupancy cleared"
|
||||
@@ -54,6 +58,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::occupancy::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::occupancy::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Occupancy detected"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
detected:
|
||||
fields: *trigger_common_fields
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_home": {
|
||||
@@ -77,6 +78,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::person::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::person::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Entered home"
|
||||
@@ -86,6 +90,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::person::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::person::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Left home"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
entered_home: *trigger_common
|
||||
left_home: *trigger_common
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -51,6 +52,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::power::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::power::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::power::common::trigger_threshold_name%]"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.power_units: &power_units
|
||||
- "mW"
|
||||
@@ -49,6 +54,7 @@ crossed_threshold:
|
||||
target: *trigger_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_off": {
|
||||
@@ -159,6 +160,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::remote::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::remote::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Remote turned off"
|
||||
@@ -168,6 +172,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::remote::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::remote::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Remote turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
turned_off: *trigger_common
|
||||
turned_on: *trigger_common
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_off": {
|
||||
@@ -76,6 +77,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::schedule::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::schedule::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Schedule block ended"
|
||||
@@ -85,6 +89,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::schedule::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::schedule::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Schedule block started"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
turned_off: *trigger_common
|
||||
turned_on: *trigger_common
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_off": {
|
||||
@@ -87,6 +88,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::siren::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::siren::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Siren turned off"
|
||||
@@ -96,6 +100,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::siren::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::siren::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Siren turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
turned_off: *trigger_common
|
||||
turned_on: *trigger_common
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_off": {
|
||||
@@ -101,6 +102,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::switch::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::switch::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Switch turned off"
|
||||
@@ -110,6 +114,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::switch::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::switch::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Switch turned on"
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
turned_off: *trigger_common
|
||||
turned_on: *trigger_common
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -51,6 +52,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::temperature::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::temperature::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::temperature::common::trigger_threshold_name%]"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.temperature_units: &temperature_units
|
||||
- "°C"
|
||||
@@ -47,6 +52,7 @@ crossed_threshold:
|
||||
target: *trigger_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_available": {
|
||||
@@ -125,6 +126,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::update::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::update::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Update became available"
|
||||
|
||||
@@ -13,5 +13,10 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
update_became_available: *trigger_common
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable, Coroutine, Sequence
|
||||
import dataclasses
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import os
|
||||
@@ -167,6 +168,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
await usb_discovery.async_setup()
|
||||
hass.data[_USB_DATA] = usb_discovery
|
||||
websocket_api.async_register_command(hass, websocket_usb_scan)
|
||||
websocket_api.async_register_command(hass, websocket_usb_list_serial_ports)
|
||||
|
||||
return True
|
||||
|
||||
@@ -481,3 +483,23 @@ async def websocket_usb_scan(
|
||||
"""Scan for new usb devices."""
|
||||
await async_request_scan(hass)
|
||||
connection.send_result(msg["id"])
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command({vol.Required("type"): "usb/list_serial_ports"})
|
||||
@websocket_api.async_response
|
||||
async def websocket_usb_list_serial_ports(
|
||||
hass: HomeAssistant,
|
||||
connection: ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""List available serial ports."""
|
||||
try:
|
||||
ports = await async_scan_serial_ports(hass)
|
||||
except OSError as err:
|
||||
connection.send_error(msg["id"], websocket_api.ERR_UNKNOWN_ERROR, str(err))
|
||||
return
|
||||
connection.send_result(
|
||||
msg["id"],
|
||||
[dataclasses.asdict(port) for port in ports],
|
||||
)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_cleaning": {
|
||||
@@ -190,6 +191,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::vacuum::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::vacuum::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Vacuum returned to dock"
|
||||
@@ -199,6 +203,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::vacuum::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::vacuum::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Vacuum encountered an error"
|
||||
@@ -208,6 +215,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::vacuum::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::vacuum::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Vacuum cleaner paused cleaning"
|
||||
@@ -217,6 +227,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::vacuum::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::vacuum::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Vacuum cleaner started cleaning"
|
||||
@@ -226,6 +239,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::vacuum::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::vacuum::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Vacuum cleaner started returning to dock"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- last
|
||||
- any
|
||||
translation_key: trigger_behavior
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
docked: *trigger_common
|
||||
errored: *trigger_common
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_closed": {
|
||||
@@ -96,6 +97,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::valve::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::valve::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Valve closed"
|
||||
@@ -105,6 +109,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::valve::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::valve::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Valve opened"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
closed: *trigger_common
|
||||
opened: *trigger_common
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"condition_threshold_name": "Threshold type",
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least",
|
||||
"trigger_threshold_name": "Threshold type"
|
||||
},
|
||||
"conditions": {
|
||||
@@ -184,6 +185,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::water_heater::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::water_heater::common::trigger_for_name%]"
|
||||
},
|
||||
"operation_mode": {
|
||||
"description": "The operation modes to trigger on.",
|
||||
"name": "Operation mode"
|
||||
@@ -206,6 +210,9 @@
|
||||
"behavior": {
|
||||
"name": "[%key:component::water_heater::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::water_heater::common::trigger_for_name%]"
|
||||
},
|
||||
"threshold": {
|
||||
"name": "[%key:component::water_heater::common::trigger_threshold_name%]"
|
||||
}
|
||||
@@ -217,6 +224,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::water_heater::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::water_heater::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Water heater turned off"
|
||||
@@ -226,6 +236,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::water_heater::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::water_heater::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Water heater turned on"
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for: &trigger_for
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
.temperature_units: &temperature_units
|
||||
- "°C"
|
||||
@@ -30,6 +35,7 @@ operation_mode_changed:
|
||||
target: *trigger_water_heater_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
operation_mode:
|
||||
context:
|
||||
filter_target: target
|
||||
@@ -62,6 +68,7 @@ target_temperature_crossed_threshold:
|
||||
target: *trigger_water_heater_target
|
||||
fields:
|
||||
behavior: *trigger_behavior
|
||||
for: *trigger_for
|
||||
threshold:
|
||||
required: true
|
||||
selector:
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["waterfurnace"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["waterfurnace==1.6.2"]
|
||||
"requirements": ["waterfurnace==1.6.5"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"condition_behavior_name": "Condition passes if",
|
||||
"trigger_behavior_name": "Trigger when"
|
||||
"trigger_behavior_name": "Trigger when",
|
||||
"trigger_for_name": "For at least"
|
||||
},
|
||||
"conditions": {
|
||||
"is_closed": {
|
||||
@@ -45,6 +46,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::window::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::window::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Window closed"
|
||||
@@ -54,6 +58,9 @@
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"name": "[%key:component::window::common::trigger_behavior_name%]"
|
||||
},
|
||||
"for": {
|
||||
"name": "[%key:component::window::common::trigger_for_name%]"
|
||||
}
|
||||
},
|
||||
"name": "Window opened"
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
for:
|
||||
required: true
|
||||
default: 00:00:00
|
||||
selector:
|
||||
duration:
|
||||
|
||||
closed:
|
||||
fields: *trigger_common_fields
|
||||
|
||||
@@ -1771,6 +1771,28 @@ class SelectSelector(Selector[SelectSelectorConfig]):
|
||||
return [parent_schema(vol.Schema(str)(val)) for val in data]
|
||||
|
||||
|
||||
class SerialSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a serial selector config."""
|
||||
|
||||
|
||||
@SELECTORS.register("serial")
|
||||
class SerialSelector(Selector[SerialSelectorConfig]):
|
||||
"""Selector for a serial port."""
|
||||
|
||||
selector_type = "serial"
|
||||
|
||||
CONFIG_SCHEMA = make_selector_config_schema()
|
||||
|
||||
def __init__(self, config: SerialSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
super().__init__(config)
|
||||
|
||||
def __call__(self, data: Any) -> str:
|
||||
"""Validate the passed selection."""
|
||||
serial: str = vol.Schema(str)(data)
|
||||
return serial
|
||||
|
||||
|
||||
class StateSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an state selector config."""
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ async-interrupt==1.2.2
|
||||
async-upnp-client==0.46.2
|
||||
atomicwrites-homeassistant==1.4.1
|
||||
attrs==26.1.0
|
||||
audioop-lts==0.2.1
|
||||
audioop-lts==0.2.2
|
||||
av==16.0.1
|
||||
awesomeversion==25.8.0
|
||||
bcrypt==5.0.0
|
||||
|
||||
@@ -38,7 +38,7 @@ dependencies = [
|
||||
"async-interrupt==1.2.2",
|
||||
"attrs==26.1.0",
|
||||
"atomicwrites-homeassistant==1.4.1",
|
||||
"audioop-lts==0.2.1",
|
||||
"audioop-lts==0.2.2",
|
||||
"awesomeversion==25.8.0",
|
||||
"bcrypt==5.0.0",
|
||||
"certifi>=2021.5.30",
|
||||
|
||||
4
requirements.txt
generated
4
requirements.txt
generated
@@ -15,7 +15,7 @@ astral==2.2
|
||||
async-interrupt==1.2.2
|
||||
atomicwrites-homeassistant==1.4.1
|
||||
attrs==26.1.0
|
||||
audioop-lts==0.2.1
|
||||
audioop-lts==0.2.2
|
||||
awesomeversion==25.8.0
|
||||
bcrypt==5.0.0
|
||||
certifi>=2021.5.30
|
||||
@@ -30,7 +30,7 @@ home-assistant-bluetooth==1.13.1
|
||||
home-assistant-intents==2026.3.24
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
infrared-protocols==1.1.0
|
||||
infrared-protocols==1.2.0
|
||||
Jinja2==3.1.6
|
||||
lru-dict==1.3.0
|
||||
mutagen==1.47.0
|
||||
|
||||
6
requirements_all.txt
generated
6
requirements_all.txt
generated
@@ -1328,7 +1328,7 @@ influxdb-client==1.50.0
|
||||
influxdb==5.3.1
|
||||
|
||||
# homeassistant.components.infrared
|
||||
infrared-protocols==1.1.0
|
||||
infrared-protocols==1.2.0
|
||||
|
||||
# homeassistant.components.inkbird
|
||||
inkbird-ble==1.1.1
|
||||
@@ -2562,7 +2562,7 @@ python-digitalocean==1.13.2
|
||||
python-dropbox-api==0.1.3
|
||||
|
||||
# homeassistant.components.duco
|
||||
python-duco-client==0.3.0
|
||||
python-duco-client==0.3.1
|
||||
|
||||
# homeassistant.components.ecobee
|
||||
python-ecobee-api==0.3.2
|
||||
@@ -3273,7 +3273,7 @@ wallbox==0.9.0
|
||||
watchdog==6.0.0
|
||||
|
||||
# homeassistant.components.waterfurnace
|
||||
waterfurnace==1.6.2
|
||||
waterfurnace==1.6.5
|
||||
|
||||
# homeassistant.components.watergate
|
||||
watergate-local-api==2025.1.0
|
||||
|
||||
6
requirements_test_all.txt
generated
6
requirements_test_all.txt
generated
@@ -1180,7 +1180,7 @@ influxdb-client==1.50.0
|
||||
influxdb==5.3.1
|
||||
|
||||
# homeassistant.components.infrared
|
||||
infrared-protocols==1.1.0
|
||||
infrared-protocols==1.2.0
|
||||
|
||||
# homeassistant.components.inkbird
|
||||
inkbird-ble==1.1.1
|
||||
@@ -2185,7 +2185,7 @@ python-bsblan==5.1.4
|
||||
python-dropbox-api==0.1.3
|
||||
|
||||
# homeassistant.components.duco
|
||||
python-duco-client==0.3.0
|
||||
python-duco-client==0.3.1
|
||||
|
||||
# homeassistant.components.ecobee
|
||||
python-ecobee-api==0.3.2
|
||||
@@ -2773,7 +2773,7 @@ wallbox==0.9.0
|
||||
watchdog==6.0.0
|
||||
|
||||
# homeassistant.components.waterfurnace
|
||||
waterfurnace==1.6.2
|
||||
waterfurnace==1.6.5
|
||||
|
||||
# homeassistant.components.watergate
|
||||
watergate-local-api==2025.1.0
|
||||
|
||||
@@ -14,11 +14,8 @@ from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.components.usb import (
|
||||
async_request_scan,
|
||||
force_usb_polling_watcher, # noqa: F401
|
||||
patch_scanned_serial_ports,
|
||||
)
|
||||
from tests.components.usb import async_request_scan, patch_scanned_serial_ports
|
||||
from tests.components.usb.conftest import force_usb_polling_watcher # noqa: F401
|
||||
|
||||
|
||||
async def test_setup_fails_on_missing_usb_port(hass: HomeAssistant) -> None:
|
||||
|
||||
@@ -26,11 +26,8 @@ from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.components.usb import (
|
||||
async_request_scan,
|
||||
force_usb_polling_watcher, # noqa: F401
|
||||
patch_scanned_serial_ports,
|
||||
)
|
||||
from tests.components.usb import async_request_scan, patch_scanned_serial_ports
|
||||
from tests.components.usb.conftest import force_usb_polling_watcher # noqa: F401
|
||||
|
||||
|
||||
async def test_config_entry_migration_v2(hass: HomeAssistant) -> None:
|
||||
|
||||
@@ -2,23 +2,10 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from aiousbwatcher import InotifyNotAvailableError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.usb import async_request_scan as usb_async_request_scan
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
@pytest.fixture(name="force_usb_polling_watcher")
|
||||
def force_usb_polling_watcher():
|
||||
"""Patch the USB integration to not use inotify and fall back to polling."""
|
||||
with patch(
|
||||
"homeassistant.components.usb.AIOUSBWatcher.async_start",
|
||||
side_effect=InotifyNotAvailableError,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
def patch_scanned_serial_ports(**kwargs) -> None:
|
||||
"""Patch the USB integration's list of scanned serial ports."""
|
||||
return patch("homeassistant.components.usb.utils.scan_serial_ports", **kwargs)
|
||||
|
||||
36
tests/components/usb/conftest.py
Normal file
36
tests/components/usb/conftest.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Fixtures for USB Discovery integration tests."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from aiousbwatcher import InotifyNotAvailableError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.usb import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import patch_scanned_serial_ports
|
||||
|
||||
|
||||
@pytest.fixture(name="force_usb_polling_watcher")
|
||||
def force_usb_polling_watcher():
|
||||
"""Patch the USB integration to not use inotify and fall back to polling."""
|
||||
with patch(
|
||||
"homeassistant.components.usb.AIOUSBWatcher.async_start",
|
||||
side_effect=InotifyNotAvailableError,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="setup_usb")
|
||||
async def setup_usb_fixture(
|
||||
hass: HomeAssistant, force_usb_polling_watcher: None
|
||||
) -> MagicMock:
|
||||
"""Set up USB integration and return the scanned serial ports mock."""
|
||||
with (
|
||||
patch("homeassistant.components.usb.async_get_usb", return_value=[]),
|
||||
patch_scanned_serial_ports(return_value=[]) as mock_serial_ports,
|
||||
):
|
||||
assert await async_setup_component(hass, DOMAIN, {"usb": {}})
|
||||
await hass.async_block_till_done()
|
||||
yield mock_serial_ports
|
||||
@@ -22,13 +22,11 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import (
|
||||
force_usb_polling_watcher, # noqa: F401
|
||||
patch_scanned_serial_ports,
|
||||
)
|
||||
from . import patch_scanned_serial_ports
|
||||
|
||||
from tests.common import (
|
||||
MockModule,
|
||||
MockUser,
|
||||
async_fire_time_changed,
|
||||
mock_config_flow,
|
||||
mock_integration,
|
||||
@@ -1590,3 +1588,83 @@ async def test_removal_aborts_discovery_flows(
|
||||
final_flows = hass.config_entries.flow.async_progress()
|
||||
assert len(final_flows) == 1
|
||||
assert final_flows[0]["handler"] == "test2"
|
||||
|
||||
|
||||
async def test_list_serial_ports(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
setup_usb: MagicMock,
|
||||
) -> None:
|
||||
"""Test listing serial ports via websocket."""
|
||||
setup_usb.return_value = [
|
||||
USBDevice(
|
||||
device="/dev/ttyUSB0",
|
||||
vid="10C4",
|
||||
pid="EA60",
|
||||
serial_number="001234",
|
||||
manufacturer="Silicon Labs",
|
||||
description="CP2102 USB to UART",
|
||||
),
|
||||
SerialDevice(
|
||||
device="/dev/ttyS0",
|
||||
serial_number=None,
|
||||
manufacturer=None,
|
||||
description="ttyS0",
|
||||
),
|
||||
]
|
||||
|
||||
ws_client = await hass_ws_client(hass)
|
||||
await ws_client.send_json({"id": 1, "type": "usb/list_serial_ports"})
|
||||
response = await ws_client.receive_json()
|
||||
|
||||
assert response["success"]
|
||||
result = response["result"]
|
||||
assert len(result) == 2
|
||||
|
||||
assert result[0]["device"] == "/dev/ttyUSB0"
|
||||
assert result[0]["vid"] == "10C4"
|
||||
assert result[0]["pid"] == "EA60"
|
||||
assert result[0]["serial_number"] == "001234"
|
||||
assert result[0]["manufacturer"] == "Silicon Labs"
|
||||
assert result[0]["description"] == "CP2102 USB to UART"
|
||||
|
||||
assert result[1]["device"] == "/dev/ttyS0"
|
||||
assert result[1]["serial_number"] is None
|
||||
assert result[1]["manufacturer"] is None
|
||||
assert result[1]["description"] == "ttyS0"
|
||||
assert "vid" not in result[1]
|
||||
assert "pid" not in result[1]
|
||||
|
||||
|
||||
async def test_list_serial_ports_require_admin(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_admin_user: MockUser,
|
||||
setup_usb: MagicMock,
|
||||
) -> None:
|
||||
"""Test that listing serial ports requires admin."""
|
||||
hass_admin_user.groups = []
|
||||
|
||||
ws_client = await hass_ws_client(hass)
|
||||
await ws_client.send_json({"id": 1, "type": "usb/list_serial_ports"})
|
||||
response = await ws_client.receive_json()
|
||||
|
||||
assert not response["success"]
|
||||
assert response["error"]["code"] == "unauthorized"
|
||||
|
||||
|
||||
async def test_list_serial_ports_os_error(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
setup_usb: MagicMock,
|
||||
) -> None:
|
||||
"""Test listing serial ports handles OSError."""
|
||||
setup_usb.side_effect = OSError("Permission denied")
|
||||
|
||||
ws_client = await hass_ws_client(hass)
|
||||
await ws_client.send_json({"id": 1, "type": "usb/list_serial_ports"})
|
||||
response = await ws_client.receive_json()
|
||||
|
||||
assert not response["success"]
|
||||
assert response["error"]["code"] == "unknown_error"
|
||||
assert "Permission denied" in response["error"]["message"]
|
||||
|
||||
@@ -124,23 +124,25 @@ async def test_setup_does_discovery(
|
||||
async def test_set_scan_interval_via_config(hass: HomeAssistant) -> None:
|
||||
"""Test the setting of the scan interval via configuration."""
|
||||
|
||||
def platform_setup(
|
||||
async def async_platform_setup(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Test the platform setup."""
|
||||
add_entities([MockEntity(should_poll=True)])
|
||||
async_add_entities([MockEntity(should_poll=True)])
|
||||
|
||||
mock_platform(
|
||||
hass, "platform.test_domain", MockPlatform(setup_platform=platform_setup)
|
||||
hass,
|
||||
"platform.test_domain",
|
||||
MockPlatform(async_setup_platform=async_platform_setup),
|
||||
)
|
||||
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
|
||||
with patch.object(hass.loop, "call_later") as mock_track:
|
||||
component.setup(
|
||||
await component.async_setup(
|
||||
{DOMAIN: {"platform": "platform", "scan_interval": timedelta(seconds=30)}}
|
||||
)
|
||||
|
||||
@@ -152,22 +154,24 @@ async def test_set_scan_interval_via_config(hass: HomeAssistant) -> None:
|
||||
async def test_set_entity_namespace_via_config(hass: HomeAssistant) -> None:
|
||||
"""Test setting an entity namespace."""
|
||||
|
||||
def platform_setup(
|
||||
async def async_platform_setup(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Test the platform setup."""
|
||||
add_entities([MockEntity(name="beer"), MockEntity(name=None)])
|
||||
async_add_entities([MockEntity(name="beer"), MockEntity(name=None)])
|
||||
|
||||
platform = MockPlatform(setup_platform=platform_setup)
|
||||
platform = MockPlatform(async_setup_platform=async_platform_setup)
|
||||
|
||||
mock_platform(hass, "platform.test_domain", platform)
|
||||
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
|
||||
component.setup({DOMAIN: {"platform": "platform", "entity_namespace": "yummy"}})
|
||||
await component.async_setup(
|
||||
{DOMAIN: {"platform": "platform", "entity_namespace": "yummy"}}
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@@ -270,8 +270,8 @@ async def test_adding_entities_with_generator_and_thread_callback(
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("disable_translations_once")
|
||||
async def test_platform_warn_slow_setup(hass: HomeAssistant) -> None:
|
||||
"""Warn we log when platform setup takes a long time."""
|
||||
async def test_platform_slow_setup_cancel_warning(hass: HomeAssistant) -> None:
|
||||
"""Test slow setup warning timer is scheduled and cancelled on success."""
|
||||
platform = MockPlatform()
|
||||
|
||||
mock_platform(hass, "platform.test_domain", platform)
|
||||
@@ -283,22 +283,26 @@ async def test_platform_warn_slow_setup(hass: HomeAssistant) -> None:
|
||||
await hass.async_block_till_done()
|
||||
assert mock_call.called
|
||||
|
||||
# mock_calls[3] is the warning message for component setup
|
||||
# mock_calls[10] is the warning message for platform setup
|
||||
timeout, logger_method = mock_call.mock_calls[10][1][:2]
|
||||
# Find the platform setup warning by matching the exact format string
|
||||
platform_warn_call = next(
|
||||
call
|
||||
for call in mock_call.call_args_list
|
||||
if len(call[0]) >= 3
|
||||
and call[0][1] == _LOGGER.warning
|
||||
and call[0][2] == "Setup of %s platform %s is taking over %s seconds."
|
||||
)
|
||||
scheduled_time = platform_warn_call[0][0]
|
||||
|
||||
assert timeout - hass.loop.time() == pytest.approx(
|
||||
assert scheduled_time - hass.loop.time() == pytest.approx(
|
||||
entity_platform.SLOW_SETUP_WARNING, 0.5
|
||||
)
|
||||
assert logger_method == _LOGGER.warning
|
||||
|
||||
assert mock_call().cancel.called
|
||||
assert mock_call.return_value.cancel.called
|
||||
|
||||
|
||||
async def test_platform_error_slow_setup(
|
||||
async def test_platform_slow_setup_timeout(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Don't block startup more than SLOW_SETUP_MAX_WAIT."""
|
||||
"""Test that platform setup is aborted after SLOW_SETUP_MAX_WAIT."""
|
||||
with patch.object(entity_platform, "SLOW_SETUP_MAX_WAIT", 0):
|
||||
called = []
|
||||
|
||||
|
||||
@@ -1122,6 +1122,22 @@ def test_state_selector_schema(schema, valid_selections, invalid_selections) ->
|
||||
_test_selector("state", schema, valid_selections, invalid_selections)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("schema", "valid_selections", "invalid_selections"),
|
||||
[
|
||||
(None, ("/dev/ttyUSB0", "/dev/ttyACM1", "COM3"), (None, 1, True)),
|
||||
({}, ("/dev/ttyUSB0",), (None,)),
|
||||
],
|
||||
)
|
||||
def test_serial_selector_schema(
|
||||
schema: dict | None,
|
||||
valid_selections: tuple[Any, ...],
|
||||
invalid_selections: tuple[Any, ...],
|
||||
) -> None:
|
||||
"""Test serial selector."""
|
||||
_test_selector("serial", schema, valid_selections, invalid_selections)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("schema", "valid_selections", "invalid_selections"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user