Compare commits
21 Commits
fd0769fd12
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b6387c0acc | |||
| 2b2261e248 | |||
| 89a2f55583 | |||
| 976b829a8d | |||
| e7e11e3429 | |||
| e5d99438d8 | |||
| 426c6b79cf | |||
| dd27cdc952 | |||
| 30965c2a8a | |||
| cb2e70ee57 | |||
| 2e8dd176b8 | |||
| d9744ff8f1 | |||
| bf1445759d | |||
| 3087ad1f80 | |||
| 6310e39fd9 | |||
| 85fb207ca0 | |||
| 757ece983c | |||
| eca30960f9 | |||
| ec2fb31068 | |||
| 8cc9e31ce6 | |||
| 9607e6e78d |
Submodule apps/ad_toolbox updated: 56e3f6e9e0...b997836401
@@ -17,7 +17,7 @@ heating_sensors:
|
|||||||
value: 18
|
value: 18
|
||||||
default:
|
default:
|
||||||
conditions: True
|
conditions: True
|
||||||
value: 27
|
value: input_number.heatpump_temperature
|
||||||
|
|
||||||
smart_heating_templates_library:
|
smart_heating_templates_library:
|
||||||
module: templatelibrary
|
module: templatelibrary
|
||||||
@@ -27,7 +27,7 @@ smart_heating_templates_library:
|
|||||||
templates_library:
|
templates_library:
|
||||||
clim_conditions:
|
clim_conditions:
|
||||||
local_expressions:
|
local_expressions:
|
||||||
temp_threshold: "sleeping_in_the_room ? 0 : 1"
|
temp_threshold: "sleeping_in_the_room ? 0 : (ext_temperature >= 35 ? 0.5 : 1)"
|
||||||
trigger_conditions:
|
trigger_conditions:
|
||||||
- "not binary_sensor.heatpump_winter_mode and (ext_temperature >= room_temperature or ext_temperature >= target_temperature or cant_open_window) and room_temperature >= (quick_resume ? target_temperature : (target_temperature + temp_threshold))"
|
- "not binary_sensor.heatpump_winter_mode and (ext_temperature >= room_temperature or ext_temperature >= target_temperature or cant_open_window) and room_temperature >= (quick_resume ? target_temperature : (target_temperature + temp_threshold))"
|
||||||
- not binary_sensor.heatpump_winter_mode and self and (ext_temperature >= target_temperature or cant_open_window)
|
- not binary_sensor.heatpump_winter_mode and self and (ext_temperature >= target_temperature or cant_open_window)
|
||||||
@@ -35,7 +35,7 @@ smart_heating_templates_library:
|
|||||||
- binary_sensor.heatpump_winter_mode and self and room_temperature < target_temperature
|
- binary_sensor.heatpump_winter_mode and self and room_temperature < target_temperature
|
||||||
blocking_conditions:
|
blocking_conditions:
|
||||||
- binary_sensor.is_everybody_sleeping and not sleeping_in_the_room and not stay_on_while_sleeping
|
- binary_sensor.is_everybody_sleeping and not sleeping_in_the_room and not stay_on_while_sleeping
|
||||||
- not room_occupancy
|
- not room_occupancy and not (sleeping_in_the_room or (occupied_tonight and binary_sensor.day_interval_night))
|
||||||
- not binary_sensor.someone_home
|
- not binary_sensor.someone_home
|
||||||
- window_open
|
- window_open
|
||||||
|
|
||||||
@@ -55,15 +55,16 @@ desk_heatpump:
|
|||||||
cant_open_window: binary_sensor.is_desk_sleeping
|
cant_open_window: binary_sensor.is_desk_sleeping
|
||||||
sleeping_in_the_room: binary_sensor.is_desk_sleeping
|
sleeping_in_the_room: binary_sensor.is_desk_sleeping
|
||||||
quick_resume: binary_sensor.desk_heatpump_quick_resume
|
quick_resume: binary_sensor.desk_heatpump_quick_resume
|
||||||
|
occupied_tonight: input_boolean.desk_occupied_tonight
|
||||||
stay_on_while_sleeping: false
|
stay_on_while_sleeping: false
|
||||||
|
|
||||||
virtual_sensors:
|
virtual_sensors:
|
||||||
sensors:
|
sensors:
|
||||||
value_selector.desk_heatpump_fan_mode:
|
value_selector.desk_heatpump_fan_mode:
|
||||||
auto: binary_sensor.heatpump_winter_mode
|
auto: binary_sensor.heatpump_winter_mode or room_temperature > target_temperature + 1
|
||||||
quiet: True
|
quiet: True
|
||||||
|
|
||||||
target_temperature: sensor.target_temperature_heatpump + 0.5
|
target_temperature: sensor.target_temperature_heatpump
|
||||||
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
||||||
fan_mode: sensor.desk_heatpump_fan_mode
|
fan_mode: sensor.desk_heatpump_fan_mode
|
||||||
|
|
||||||
@@ -101,15 +102,16 @@ bedroom_heatpump:
|
|||||||
cant_open_window: binary_sensor.is_bedroom_sleeping
|
cant_open_window: binary_sensor.is_bedroom_sleeping
|
||||||
sleeping_in_the_room: binary_sensor.is_bedroom_sleeping
|
sleeping_in_the_room: binary_sensor.is_bedroom_sleeping
|
||||||
quick_resume: binary_sensor.bedroom_heatpump_quick_resume
|
quick_resume: binary_sensor.bedroom_heatpump_quick_resume
|
||||||
stay_on_while_sleeping: true
|
occupied_tonight: input_boolean.bedroom_occupied_tonight
|
||||||
|
stay_on_while_sleeping: false
|
||||||
|
|
||||||
virtual_sensors:
|
virtual_sensors:
|
||||||
sensors:
|
sensors:
|
||||||
value_selector.bedroom_heatpump_fan_mode:
|
value_selector.bedroom_heatpump_fan_mode:
|
||||||
auto: binary_sensor.heatpump_winter_mode
|
auto: binary_sensor.heatpump_winter_mode or room_temperature > target_temperature + 1
|
||||||
quiet: True
|
quiet: True
|
||||||
|
|
||||||
target_temperature: sensor.target_temperature_heatpump + 0.5
|
target_temperature: sensor.target_temperature_heatpump # + 0.5
|
||||||
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
||||||
fan_mode: sensor.bedroom_heatpump_fan_mode
|
fan_mode: sensor.bedroom_heatpump_fan_mode
|
||||||
|
|
||||||
@@ -137,6 +139,10 @@ living_room_heatpump:
|
|||||||
virtual_sensors:
|
virtual_sensors:
|
||||||
sensors:
|
sensors:
|
||||||
binary_sensor.living_room_any_window_open: binary_sensor.living_room_french_door or binary_sensor.living_room_window
|
binary_sensor.living_room_any_window_open: binary_sensor.living_room_french_door or binary_sensor.living_room_window
|
||||||
|
binary_sensor.living_room_cant_open_window: binary_sensor.is_living_room_sleeping or binary_sensor.projector_on
|
||||||
|
value_selector.living_room_heatpump_fan_mode:
|
||||||
|
auto: binary_sensor.heatpump_winter_mode or room_temperature > target_temperature + 1
|
||||||
|
quiet: True
|
||||||
|
|
||||||
constants:
|
constants:
|
||||||
window_open: binary_sensor.living_room_any_window_open
|
window_open: binary_sensor.living_room_any_window_open
|
||||||
@@ -144,13 +150,15 @@ living_room_heatpump:
|
|||||||
room_temperature: sensor.living_room_temperature
|
room_temperature: sensor.living_room_temperature
|
||||||
room_occupancy: binary_sensor.living_room_occupancy
|
room_occupancy: binary_sensor.living_room_occupancy
|
||||||
target_temperature: sensor.target_temperature_heatpump
|
target_temperature: sensor.target_temperature_heatpump
|
||||||
cant_open_window: binary_sensor.is_living_room_sleeping
|
cant_open_window: binary_sensor.living_room_cant_open_window
|
||||||
sleeping_in_the_room: binary_sensor.is_living_room_sleeping
|
sleeping_in_the_room: binary_sensor.is_living_room_sleeping
|
||||||
quick_resume: binary_sensor.living_room_heatpump_quick_resume
|
quick_resume: binary_sensor.living_room_heatpump_quick_resume
|
||||||
|
occupied_tonight: input_boolean.living_room_occupied_tonight
|
||||||
stay_on_while_sleeping: false
|
stay_on_while_sleeping: false
|
||||||
|
|
||||||
target_temperature: sensor.target_temperature_heatpump + 0.5
|
target_temperature: sensor.target_temperature_heatpump + 0.5
|
||||||
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
||||||
|
fan_mode: sensor.living_room_heatpump_fan_mode
|
||||||
|
|
||||||
smart_conditions:
|
smart_conditions:
|
||||||
callback_delay: 10 # on the morning it can happen that we ask to start and stop the heat pump immediately afterwards which in result in the heat pump ignoring the second command
|
callback_delay: 10 # on the morning it can happen that we ask to start and stop the heat pump immediately afterwards which in result in the heat pump ignoring the second command
|
||||||
|
|||||||
174
apps/global/home_alarm.yaml
Normal file
174
apps/global/home_alarm.yaml
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
home_alarm:
|
||||||
|
module: homealarm
|
||||||
|
class: HomeAlarm
|
||||||
|
|
||||||
|
smart_conditions:
|
||||||
|
trigger_conditions: not binary_sensor.someone_home or input_select.alarm_mode == 'On'
|
||||||
|
blocking_conditions: input_select.alarm_mode == 'Off'
|
||||||
|
|
||||||
|
opening_sensors:
|
||||||
|
- binary_sensor.group_doors
|
||||||
|
- binary_sensor.group_windows
|
||||||
|
|
||||||
|
motion_sensors:
|
||||||
|
- binary_sensor.corridor_motion
|
||||||
|
- binary_sensor.hallway_motion
|
||||||
|
- binary_sensor.restroom_motion
|
||||||
|
- binary_sensor.garage_motion
|
||||||
|
- binary_sensor.kitchen_motion
|
||||||
|
- binary_sensor.mezzanine_motion
|
||||||
|
- binary_sensor.living_room_presence_presence
|
||||||
|
|
||||||
|
motion_intrusion_increment: 0.2
|
||||||
|
opening_intrusion_increment: 0.9
|
||||||
|
intrusion_cooldown_step: 0.1
|
||||||
|
intrusion_cooldown_interval: 60
|
||||||
|
|
||||||
|
reset_button: input_button.alarm_reset
|
||||||
|
|
||||||
|
presence_simulation:
|
||||||
|
smart_conditions: input_boolean.simulate_presence and binary_sensor.home_alarm_armed
|
||||||
|
|
||||||
|
target_apps:
|
||||||
|
restroom:
|
||||||
|
# 5 minutes every 2 hours with 1 hour jitter
|
||||||
|
app: light_restroom
|
||||||
|
mode: method
|
||||||
|
start_payload:
|
||||||
|
brightness_pct: 100
|
||||||
|
transition: 1
|
||||||
|
stop_payload:
|
||||||
|
transition: 1
|
||||||
|
on_duration_min: 5
|
||||||
|
on_jitter_min: 1
|
||||||
|
interval_min: 120
|
||||||
|
interval_jitter_min: 60
|
||||||
|
start_delay_min: 60
|
||||||
|
start_delay_jitter_min: 20
|
||||||
|
|
||||||
|
light_living_room:
|
||||||
|
# Exemple "bureau toute la soiree": longues periodes allumees le soir
|
||||||
|
# on_duration_min / interval_min are optional:
|
||||||
|
# if omitted, the app stays active for the whole simulation window
|
||||||
|
# (still gated by smart_conditions + time_window).
|
||||||
|
app: light_living_room
|
||||||
|
status_sensor: binary_sensor.home_alarm_presence_mezzanine
|
||||||
|
mode: method
|
||||||
|
smart_conditions: binary_sensor.is_dark_outside
|
||||||
|
time_window:
|
||||||
|
stop: '23:30'
|
||||||
|
stop_jitter_min: 30
|
||||||
|
start_payload:
|
||||||
|
brightness_pct: 100
|
||||||
|
transition: 2
|
||||||
|
stop_payload:
|
||||||
|
transition: 2
|
||||||
|
|
||||||
|
home_alarm_notifications:
|
||||||
|
module: notificationsmanager
|
||||||
|
class: NotificationsManager
|
||||||
|
|
||||||
|
priority: 100 # default priority app is 50, since the notification_manager doesn't create any sensor but is based on sensor created by many app, it's important it's created last
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
alarm_armed_notification:
|
||||||
|
recipients:
|
||||||
|
all:
|
||||||
|
services:
|
||||||
|
- notify/mobile_app_iphone_de_pierre
|
||||||
|
- notify/mobile_app_mae
|
||||||
|
conditions: binary_sensor.home_alarm_armed
|
||||||
|
|
||||||
|
message:
|
||||||
|
title: Alarme
|
||||||
|
content: Activation de l'alarme
|
||||||
|
timeout: 600
|
||||||
|
data:
|
||||||
|
tag: home_alarm_notification
|
||||||
|
|
||||||
|
windows_open_notification:
|
||||||
|
recipients:
|
||||||
|
all:
|
||||||
|
services:
|
||||||
|
- notify/mobile_app_iphone_de_pierre
|
||||||
|
- notify/mobile_app_mae
|
||||||
|
conditions: binary_sensor.home_alarm_armed and sensor.ad_number_of_windows_open > 0 and binary_sensor.home_alarm_just_armed
|
||||||
|
|
||||||
|
message:
|
||||||
|
title: Alarme
|
||||||
|
content: "{sensor.ad_number_of_windows_open} fenêtre(s) ouvertes"
|
||||||
|
data:
|
||||||
|
tag: home_alarm_notification
|
||||||
|
push:
|
||||||
|
interruption-level: critical
|
||||||
|
|
||||||
|
doors_open_notification:
|
||||||
|
recipients:
|
||||||
|
all:
|
||||||
|
services:
|
||||||
|
- notify/mobile_app_iphone_de_pierre
|
||||||
|
- notify/mobile_app_mae
|
||||||
|
conditions: binary_sensor.home_alarm_armed and sensor.ad_number_of_doors_open > 0 and binary_sensor.home_alarm_just_armed
|
||||||
|
|
||||||
|
message:
|
||||||
|
title: Alarme
|
||||||
|
content: "{sensor.ad_number_of_doors_open} porte(s) ouvertes"
|
||||||
|
data:
|
||||||
|
tag: home_alarm_notification
|
||||||
|
push:
|
||||||
|
interruption-level: critical
|
||||||
|
|
||||||
|
intrusion_detected_notification:
|
||||||
|
recipients:
|
||||||
|
all:
|
||||||
|
services:
|
||||||
|
- notify/mobile_app_iphone_de_pierre
|
||||||
|
- notify/mobile_app_mae
|
||||||
|
conditions: binary_sensor.home_alarm_armed and sensor.home_alarm_intrusion_level >= 1
|
||||||
|
|
||||||
|
message:
|
||||||
|
title: Alarme
|
||||||
|
content: Intrusion détectée
|
||||||
|
data:
|
||||||
|
tag: home_alarm_notification
|
||||||
|
push:
|
||||||
|
interruption-level: critical
|
||||||
|
|
||||||
|
intrusion_suspected_notification:
|
||||||
|
recipients:
|
||||||
|
all:
|
||||||
|
services:
|
||||||
|
- notify/mobile_app_iphone_de_pierre
|
||||||
|
- notify/mobile_app_mae
|
||||||
|
conditions: binary_sensor.home_alarm_armed and sensor.home_alarm_intrusion_level > 0 and sensor.home_alarm_intrusion_level < 1
|
||||||
|
|
||||||
|
message:
|
||||||
|
title: Alarme
|
||||||
|
content: Intrusion suspectée
|
||||||
|
data:
|
||||||
|
tag: home_alarm_notification
|
||||||
|
|
||||||
|
alarm_unarmed_notification:
|
||||||
|
recipients:
|
||||||
|
all:
|
||||||
|
services:
|
||||||
|
- notify/mobile_app_iphone_de_pierre
|
||||||
|
- notify/mobile_app_mae
|
||||||
|
conditions: not binary_sensor.home_alarm_armed
|
||||||
|
|
||||||
|
message:
|
||||||
|
title: Alarme
|
||||||
|
content: Désactivation de l'alarme
|
||||||
|
timeout: 600
|
||||||
|
|
||||||
|
clear_home_alarm_notification:
|
||||||
|
recipients:
|
||||||
|
all:
|
||||||
|
services:
|
||||||
|
- notify/mobile_app_iphone_de_pierre
|
||||||
|
- notify/mobile_app_mae
|
||||||
|
conditions: not binary_sensor.home_alarm_armed
|
||||||
|
message:
|
||||||
|
content: clear_notification
|
||||||
|
data:
|
||||||
|
tag: home_alarm_notification
|
||||||
@@ -13,6 +13,11 @@ motion_tracker:
|
|||||||
motion_sensors: binary_sensor.hallway_motion
|
motion_sensors: binary_sensor.hallway_motion
|
||||||
restroom:
|
restroom:
|
||||||
motion_sensors: binary_sensor.restroom_motion
|
motion_sensors: binary_sensor.restroom_motion
|
||||||
|
porche:
|
||||||
|
motion_sensors: binary_sensor.porche_motion
|
||||||
|
desk:
|
||||||
|
update_on_both_front: true
|
||||||
|
motion_sensors: binary_sensor.desk_presence_presence
|
||||||
garage:
|
garage:
|
||||||
door_generate_motion: true
|
door_generate_motion: true
|
||||||
motion_sensors:
|
motion_sensors:
|
||||||
@@ -33,6 +38,9 @@ occupancy_sensors:
|
|||||||
class: VirtualSensorsApp
|
class: VirtualSensorsApp
|
||||||
priority: 8 # default priority app is 50, since most of them require sensors created by virtual sensors, it's important that virtual_sensors start first
|
priority: 8 # default priority app is 50, since most of them require sensors created by virtual sensors, it's important that virtual_sensors start first
|
||||||
|
|
||||||
|
default_values:
|
||||||
|
binary_sensor.someone_home: off
|
||||||
|
|
||||||
sensors:
|
sensors:
|
||||||
binary_sensor.living_room_occupancy:
|
binary_sensor.living_room_occupancy:
|
||||||
trigger_conditions: sensor.living_room_last_motion <= 5
|
trigger_conditions: sensor.living_room_last_motion <= 5
|
||||||
@@ -49,5 +57,6 @@ occupancy_sensors:
|
|||||||
retain_time: 5 * 60
|
retain_time: 5 * 60
|
||||||
conditions:
|
conditions:
|
||||||
trigger_conditions:
|
trigger_conditions:
|
||||||
- binary_sensor.alienware_r10
|
- sensor.desk_last_motion <= 5
|
||||||
- sensor.macbook_m3_pro_de_pierre_frontmost_app != 'loginwindow' and sensor.macbook_m3_pro_de_pierre_displays == 2
|
#- binary_sensor.alienware_r10
|
||||||
|
#- sensor.macbook_m3_pro_de_pierre_frontmost_app != 'loginwindow' and sensor.macbook_m3_pro_de_pierre_displays == 2
|
||||||
|
|||||||
@@ -17,4 +17,6 @@ virtual_events:
|
|||||||
event_name: turn_off_all_lights
|
event_name: turn_off_all_lights
|
||||||
event_condition: binary_sensor.is_everybody_sleeping
|
event_condition: binary_sensor.is_everybody_sleeping
|
||||||
|
|
||||||
|
turn_off_all_living_room_lights:
|
||||||
|
event_name: turn_off_all_living_room_lights
|
||||||
|
event_condition: binary_sensor.is_living_room_sleeping or binary_sensor.projector_on
|
||||||
|
|||||||
@@ -4,24 +4,58 @@ virtual_sensors:
|
|||||||
priority: 10 # default priority app is 50, since most of them require sensors created by virtual sensors, it's important that virtual_sensors start first
|
priority: 10 # default priority app is 50, since most of them require sensors created by virtual sensors, it's important that virtual_sensors start first
|
||||||
|
|
||||||
sensors:
|
sensors:
|
||||||
continuous_condition.is_apple_tv_idle:
|
binary_sensor.someone_home: person.maeva == 'home' or person.pierre == 'home' or (input_boolean.use_red_shelly_presence and binary_sensor.red_shelly_home)
|
||||||
time: 5
|
|
||||||
conditions: media_player.apple_tv == 'paused' or media_player.apple_tv == 'idle' or media_player.apple_tv == 'standby'
|
|
||||||
|
|
||||||
binary_sensor.is_apple_tv_on: media_player.apple_tv != 'off'
|
|
||||||
binary_sensor.someone_home: person.maeva == 'home' or person.pierre == 'home'
|
|
||||||
binary_sensor.is_dark_outside: sun.sun == 'below_horizon'
|
binary_sensor.is_dark_outside: sun.sun == 'below_horizon'
|
||||||
|
event_sensor.red_shelly_home:
|
||||||
|
retain_time: 120
|
||||||
|
activation_events:
|
||||||
|
signal_strenght:
|
||||||
|
event_name: state_changed
|
||||||
|
event_data:
|
||||||
|
entity_id: sensor.shelly_button_red_signal_strength
|
||||||
|
|
||||||
# living room sensors
|
# living room sensors
|
||||||
value_selector.living_room_ambiance_light:
|
value_selector.living_room_ambiance_light:
|
||||||
values:
|
values:
|
||||||
0:
|
0:
|
||||||
- not binary_sensor.living_room_occupancy
|
- not binary_sensor.living_room_occupancy
|
||||||
- binary_sensor.is_apple_tv_on and not binary_sensor.is_apple_tv_idle
|
- binary_sensor.projector_on and not binary_sensor.is_vertex2_content_idle
|
||||||
- light.living_room
|
- light.living_room
|
||||||
- sensor.mezzanine_motion_light_level > 10
|
- sensor.mezzanine_motion_light_level > 15
|
||||||
1: binary_sensor.is_everybody_sleeping
|
- binary_sensor.is_living_room_sleeping
|
||||||
|
1:
|
||||||
|
- binary_sensor.is_everybody_sleeping
|
||||||
|
- binary_sensor.projector_on
|
||||||
2: true
|
2: true
|
||||||
|
|
||||||
|
# home cinema sensors
|
||||||
|
binary_sensor.projector_on: switch.living_room_projector_plug and sensor.living_room_projector_plug_power > 50
|
||||||
|
continuous_condition.projector_off_for_a_while:
|
||||||
|
time: 90
|
||||||
|
default_value: on
|
||||||
|
conditions: not binary_sensor.projector_on
|
||||||
|
# is_apple_tv_on is not reliable as the the apple tv integration sometimes return "paused" when it's actually off,
|
||||||
|
# you should use is_using_apple_tv instead for most automation
|
||||||
|
binary_sensor.is_apple_tv_on: media_player.apple_tv != 'off' and media_player.apple_tv != 'idle'
|
||||||
|
binary_sensor.is_xbox_on: media_player.xbox != 'off'
|
||||||
|
binary_sensor.is_playstation_on: switch.ps5_136_power
|
||||||
|
retain_condition.vertex2_has_input_signal:
|
||||||
|
retain_time: 10
|
||||||
|
conditions:
|
||||||
|
trigger_conditions: sensor.hdfury_vertex2_07_output_tx0 or sensor.hdfury_vertex2_07_audio_output
|
||||||
|
disable_conditions: sensor.hdfury_vertex2_07_output_tx0 == 'unavailable'
|
||||||
|
binary_sensor.is_using_apple_tv: binary_sensor.is_apple_tv_on and binary_sensor.vertex2_has_input_signal and select.hdfury_vertex2_07_port_select_tx0 == 0
|
||||||
|
binary_sensor.is_using_xbox: binary_sensor.is_xbox_on and binary_sensor.vertex2_has_input_signal and select.hdfury_vertex2_07_port_select_tx0 == 1
|
||||||
|
binary_sensor.is_using_playstation: binary_sensor.is_playstation_on and binary_sensor.vertex2_has_input_signal and select.hdfury_vertex2_07_port_select_tx0 == 2
|
||||||
|
|
||||||
|
continuous_condition.is_vertex2_content_idle:
|
||||||
|
time: 5
|
||||||
|
conditions:
|
||||||
|
trigger_conditions:
|
||||||
|
- binary_sensor.is_using_apple_tv and (media_player.apple_tv == 'paused' or media_player.apple_tv == 'idle' or media_player.apple_tv == 'standby')
|
||||||
|
# - binary_sensor.is_using_playstation and sensor.ps5_136_activity == 'idle'
|
||||||
|
# - binary_sensor.is_using_xbox and not binary_sensor.yetanotherpedro_in_game
|
||||||
|
|
||||||
#p1p sensors
|
#p1p sensors
|
||||||
binary_sensor.p1p_is_printing:
|
binary_sensor.p1p_is_printing:
|
||||||
trigger_conditions: sensor.garage_p1p_etat_de_l_impression != 'idle' and sensor.garage_p1p_etat_de_l_impression != 'finish' and sensor.garage_p1p_etat_de_l_impression != 'idle' and sensor.garage_p1p_etat_de_l_impression != 'offline'
|
trigger_conditions: sensor.garage_p1p_etat_de_l_impression != 'idle' and sensor.garage_p1p_etat_de_l_impression != 'finish' and sensor.garage_p1p_etat_de_l_impression != 'idle' and sensor.garage_p1p_etat_de_l_impression != 'offline'
|
||||||
|
|||||||
1033
apps/homealarm.py
Normal file
1033
apps/homealarm.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -62,7 +62,7 @@ class InformationsCollector(SmartObject):
|
|||||||
self.min_temp_yesterday_sensor = self.create_entity(output_sensors['min_temp_yesterday'])
|
self.min_temp_yesterday_sensor = self.create_entity(output_sensors['min_temp_yesterday'])
|
||||||
|
|
||||||
current_min_temp, current_max_temp = self.compute_min_max_temp()
|
current_min_temp, current_max_temp = self.compute_min_max_temp()
|
||||||
if not self.max_temp_sensor.exists() or self.max_temp_sensor.get_state() == 'unavailable':
|
if not self.max_temp_sensor.exists() or self.max_temp_sensor.get_state() == 'unavailable' or self.max_temp_sensor.get_state() == 'unknown':
|
||||||
max_temp = current_max_temp
|
max_temp = current_max_temp
|
||||||
try:
|
try:
|
||||||
if max_temp is None or self.dataset['max_temp'] > max_temp:
|
if max_temp is None or self.dataset['max_temp'] > max_temp:
|
||||||
@@ -70,7 +70,7 @@ class InformationsCollector(SmartObject):
|
|||||||
self.log_info(f"Restoring max_temp from dataset ({max_temp}°C)")
|
self.log_info(f"Restoring max_temp from dataset ({max_temp}°C)")
|
||||||
except KeyError: pass
|
except KeyError: pass
|
||||||
self.set_max_temp_sensor(max_temp)
|
self.set_max_temp_sensor(max_temp)
|
||||||
if not self.min_temp_sensor.exists() or self.min_temp_sensor.get_state() == 'unavailable':
|
if not self.min_temp_sensor.exists() or self.min_temp_sensor.get_state() == 'unavailable' or self.min_temp_sensor.get_state() == 'unknown':
|
||||||
min_temp = current_min_temp
|
min_temp = current_min_temp
|
||||||
try:
|
try:
|
||||||
if min_temp is None or self.dataset['min_temp'] < min_temp:
|
if min_temp is None or self.dataset['min_temp'] < min_temp:
|
||||||
@@ -199,8 +199,12 @@ class InformationsCollector(SmartObject):
|
|||||||
min_temp = None
|
min_temp = None
|
||||||
for sensor in self.args['min_max_temp_sensors']['temperature_sensors']:
|
for sensor in self.args['min_max_temp_sensors']['temperature_sensors']:
|
||||||
temp = self.get_state(sensor)
|
temp = self.get_state(sensor)
|
||||||
if not max_temp or temp > max_temp: max_temp = temp
|
try:
|
||||||
if not min_temp or temp < min_temp: min_temp = temp
|
temp = float(temp)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
continue
|
||||||
|
if max_temp is None or temp > max_temp: max_temp = temp
|
||||||
|
if min_temp is None or temp < min_temp: min_temp = temp
|
||||||
return min_temp,max_temp
|
return min_temp,max_temp
|
||||||
|
|
||||||
def on_log_error(self, name, ts, level, type, message, kwargs):
|
def on_log_error(self, name, ts, level, type, message, kwargs):
|
||||||
@@ -224,10 +228,17 @@ class InformationsCollector(SmartObject):
|
|||||||
|
|
||||||
def on_temperature_change(self, entity, attribute, old, new, kwargs):
|
def on_temperature_change(self, entity, attribute, old, new, kwargs):
|
||||||
if new != old:
|
if new != old:
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_value = float(new)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.log_warning(f"Invalid temperature value from {entity}: {new}")
|
||||||
|
return
|
||||||
|
|
||||||
min_temp = self.min_temp_sensor.get_state()
|
min_temp = self.min_temp_sensor.get_state()
|
||||||
max_temp = self.max_temp_sensor.get_state()
|
max_temp = self.max_temp_sensor.get_state()
|
||||||
if max_temp is None or max_temp == 'unavailable' or new > max_temp: self.set_max_temp_sensor(new)
|
if max_temp is None or max_temp == 'unavailable' or new_value > float(max_temp): self.set_max_temp_sensor(new_value)
|
||||||
if min_temp is None or min_temp == 'unavailable' or new < min_temp: self.set_min_temp_sensor(new)
|
if min_temp is None or min_temp == 'unavailable' or new_value < float(min_temp): self.set_min_temp_sensor(new_value)
|
||||||
|
|
||||||
def run_at_midnight(self, kwargs):
|
def run_at_midnight(self, kwargs):
|
||||||
self.max_temp_yesterday_sensor.set_state(state=self.max_temp_sensor.get_state(), attributes={'unit_of_measurement': '°C', 'device_class': 'temperature'})
|
self.max_temp_yesterday_sensor.set_state(state=self.max_temp_sensor.get_state(), attributes={'unit_of_measurement': '°C', 'device_class': 'temperature'})
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import appdaemon.plugins.hass.hassapi as hass
|
import appdaemon.plugins.hass.hassapi as hass
|
||||||
import appdaemon.exceptions as ad_exception
|
import appdaemon.exceptions as ad_exception
|
||||||
import ad_toolbox.smartcondition as SmartCondition
|
import ad_toolbox.smartcondition as SmartCondition
|
||||||
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from expressionparser import ParsingException
|
from expressionparser import ParsingException
|
||||||
|
|
||||||
class Notification:
|
class Notification:
|
||||||
|
ENTITY_PLACEHOLDER_PATTERN = re.compile(r"\{([^{}]+)\}")
|
||||||
|
|
||||||
def __init__(self,ad_api,config,name):
|
def __init__(self,ad_api,config,name):
|
||||||
self.recipients = dict()
|
self.recipients = dict()
|
||||||
self.config = config
|
self.config = config
|
||||||
@@ -50,14 +53,40 @@ class Notification:
|
|||||||
for service in recipient_yaml['services']:
|
for service in recipient_yaml['services']:
|
||||||
clear_notification(service,tag)
|
clear_notification(service,tag)
|
||||||
|
|
||||||
|
def render_message_content(self, content):
|
||||||
|
if not isinstance(content, str):
|
||||||
|
return content
|
||||||
|
|
||||||
|
def replace_entity_state(match):
|
||||||
|
entity_id = match.group(1).strip()
|
||||||
|
if entity_id == "":
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
state = self.ad_api.get_state(entity_id)
|
||||||
|
except Exception as e:
|
||||||
|
self.log_error(f"Failed to resolve '{entity_id}' in notification content: {e}")
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
if state is None:
|
||||||
|
self.log_error(f"Unable to resolve '{entity_id}' in notification content")
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
return str(state)
|
||||||
|
|
||||||
|
return self.ENTITY_PLACEHOLDER_PATTERN.sub(replace_entity_state, content)
|
||||||
|
|
||||||
def send_notification(self,recipient):
|
def send_notification(self,recipient):
|
||||||
try: title = self.config['message']['title']
|
try: title_template = self.config['message']['title']
|
||||||
except KeyError: title = ""
|
except KeyError: title_template = ""
|
||||||
try: message = self.config['message']['content']
|
try: message_content = self.config['message']['content']
|
||||||
except KeyError: message = ""
|
except KeyError: message_content = ""
|
||||||
try: data = self.config['message']['data']
|
try: data = self.config['message']['data']
|
||||||
except KeyError: data = dict()
|
except KeyError: data = dict()
|
||||||
|
|
||||||
|
title = self.render_message_content(title_template)
|
||||||
|
message = self.render_message_content(message_content)
|
||||||
|
|
||||||
#TODO : maybe it's better to not send tag when it's not needed
|
#TODO : maybe it's better to not send tag when it's not needed
|
||||||
if not 'tag' in data:
|
if not 'tag' in data:
|
||||||
data['tag'] = self.uuid
|
data['tag'] = self.uuid
|
||||||
|
|||||||
@@ -41,5 +41,14 @@ light_restroom:
|
|||||||
25: binary_sensor.day_interval_night
|
25: binary_sensor.day_interval_night
|
||||||
100: True
|
100: True
|
||||||
|
|
||||||
|
light_porche:
|
||||||
|
module: smartlight
|
||||||
|
class: SmartLight
|
||||||
|
entity: light.porche
|
||||||
|
|
||||||
|
smart_conditions:
|
||||||
|
trigger_conditions: sensor.porche_last_motion < 3
|
||||||
|
blocking_conditions: sensor.porche_motion_light_level > 10 and not self
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
6
apps/rooms/desk.yaml
Normal file
6
apps/rooms/desk.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
light_ambiance:
|
||||||
|
module: smartlight
|
||||||
|
class: SmartLight
|
||||||
|
entity: light.desk_ambiance
|
||||||
|
|
||||||
|
smart_conditions: sensor.desk_last_motion < 3 and binary_sensor.is_dark_outside
|
||||||
@@ -5,7 +5,7 @@ light_garage:
|
|||||||
|
|
||||||
smart_conditions:
|
smart_conditions:
|
||||||
trigger_conditions: sensor.garage_last_motion < 3
|
trigger_conditions: sensor.garage_last_motion < 3
|
||||||
blocking_conditions: sensor.garage_motion_light_level > 10
|
blocking_conditions: sensor.garage_motion_light_level > 10 and binary_sensor.garage_entrance
|
||||||
|
|
||||||
p1p_light:
|
p1p_light:
|
||||||
module: smartswitch
|
module: smartswitch
|
||||||
|
|||||||
@@ -90,4 +90,19 @@ senseo_quadrante:
|
|||||||
good_morning:
|
good_morning:
|
||||||
event_name: good_morning
|
event_name: good_morning
|
||||||
button: button.senseo_quadrante_brew_coffee_double
|
button: button.senseo_quadrante_brew_coffee_double
|
||||||
condition: binary_sensor.senseo_quadrante_has_program and binary_sensor.senseo_quadrante_has_program.cup_size == 2 and not binary_sensor.senseo_quadrante_has_program.power_pressed
|
condition: binary_sensor.senseo_quadrante_has_program and binary_sensor.senseo_quadrante_has_program.cup_size == 2 and not binary_sensor.senseo_quadrante_has_program.power_pressed
|
||||||
|
|
||||||
|
storeban:
|
||||||
|
module: smartshutter
|
||||||
|
class: SmartShutter
|
||||||
|
entity: cover.storeban
|
||||||
|
|
||||||
|
virtual_sensors:
|
||||||
|
sensors:
|
||||||
|
binary_sensor.storeban_autoclose: sensor.sun_solar_elevation < 0 or (sensor.sun_solar_azimuth > 235 and sensor.sun_solar_elevation < 45)
|
||||||
|
|
||||||
|
close_conditions:
|
||||||
|
trigger_conditions: binary_sensor.storeban_autoclose
|
||||||
|
disable_conditions: not binary_sensor.storeban_autoclose
|
||||||
|
|
||||||
|
|
||||||
@@ -10,10 +10,14 @@ light_mezzanine_01:
|
|||||||
- light.living_room
|
- light.living_room
|
||||||
|
|
||||||
light_brightness_pct:
|
light_brightness_pct:
|
||||||
15: binary_sensor.is_everybody_sleeping
|
15: binary_sensor.is_everybody_sleeping or binary_sensor.projector_on
|
||||||
35: binary_sensor.day_interval_night
|
35: binary_sensor.day_interval_night
|
||||||
100: True
|
100: True
|
||||||
|
|
||||||
|
off_events:
|
||||||
|
- turn_off_all_lights
|
||||||
|
- turn_off_all_living_room_lights
|
||||||
|
|
||||||
light_mezzanine_02:
|
light_mezzanine_02:
|
||||||
module: smartlight
|
module: smartlight
|
||||||
class: SmartLight
|
class: SmartLight
|
||||||
@@ -26,10 +30,14 @@ light_mezzanine_02:
|
|||||||
- light.living_room
|
- light.living_room
|
||||||
|
|
||||||
light_brightness_pct:
|
light_brightness_pct:
|
||||||
5: binary_sensor.is_everybody_sleeping
|
5: binary_sensor.is_everybody_sleeping or binary_sensor.projector_on
|
||||||
25: binary_sensor.day_interval_night
|
25: binary_sensor.day_interval_night
|
||||||
100: True
|
100: True
|
||||||
|
|
||||||
|
off_events:
|
||||||
|
- turn_off_all_lights
|
||||||
|
- turn_off_all_living_room_lights
|
||||||
|
|
||||||
light_bookshelves:
|
light_bookshelves:
|
||||||
module: smartlight
|
module: smartlight
|
||||||
class: SmartLight
|
class: SmartLight
|
||||||
@@ -76,4 +84,59 @@ light_living_room:
|
|||||||
|
|
||||||
|
|
||||||
off_events:
|
off_events:
|
||||||
- turn_off_all_lights
|
- turn_off_all_lights
|
||||||
|
- turn_off_all_living_room_lights
|
||||||
|
|
||||||
|
|
||||||
|
projection_screen:
|
||||||
|
module: smartshutter
|
||||||
|
class: SmartShutter
|
||||||
|
entity: cover.living_room_projection_screen
|
||||||
|
close_position: 22
|
||||||
|
|
||||||
|
close_conditions: # close mean screen down
|
||||||
|
# when the projector is turn back on, it can happen that this condition is evaluated before projector_off_for_a_while is updated
|
||||||
|
# so I need to test both binary_sensor.projector_on and not binary_sensor.projector_off_for_a_while
|
||||||
|
trigger_conditions: binary_sensor.projector_on or not binary_sensor.projector_off_for_a_while
|
||||||
|
disable_conditions:
|
||||||
|
- binary_sensor.day_interval_night and not binary_sensor.projector_on
|
||||||
|
- binary_sensor.is_everybody_sleeping and not binary_sensor.projector_on
|
||||||
|
|
||||||
|
bravia_theatre:
|
||||||
|
module: smartswitch
|
||||||
|
class: SmartSwitch
|
||||||
|
entity: media_player.bravia_theatre
|
||||||
|
|
||||||
|
smart_conditions: binary_sensor.vertex2_has_input_signal
|
||||||
|
|
||||||
|
w4000i:
|
||||||
|
module: smartswitch
|
||||||
|
class: SmartSwitch
|
||||||
|
entity: media_player.benq_w4000i
|
||||||
|
|
||||||
|
smart_conditions: binary_sensor.vertex2_has_input_signal
|
||||||
|
|
||||||
|
mezzanine_curtains:
|
||||||
|
module: smartshutter
|
||||||
|
class: SmartShutter
|
||||||
|
entity: cover.salon_mezzanine_curtain
|
||||||
|
|
||||||
|
close_conditions:
|
||||||
|
trigger_conditions: binary_sensor.projector_on or not binary_sensor.projector_off_for_a_while
|
||||||
|
disable_conditions: self == 'closed' and (binary_sensor.is_dark_outside or binary_sensor.is_anybody_sleeping)
|
||||||
|
|
||||||
|
living_room_sleep_switch:
|
||||||
|
module: smartswitch
|
||||||
|
class: SmartSwitch
|
||||||
|
|
||||||
|
entity: input_boolean.living_room_sleeping
|
||||||
|
|
||||||
|
toggle_events:
|
||||||
|
button_press:
|
||||||
|
event_name: state_changed
|
||||||
|
debounce_seconds: 0.5
|
||||||
|
event_data:
|
||||||
|
entity_id: event.living_room_bilresa_button_bouton_2
|
||||||
|
new_state:
|
||||||
|
attributes:
|
||||||
|
event_type: multi_press_1
|
||||||
@@ -114,6 +114,8 @@ from ad_toolbox.eventhandler import EventHandler
|
|||||||
class SmartLight(SmartSwitch):
|
class SmartLight(SmartSwitch):
|
||||||
|
|
||||||
def on_initialize_smart_object(self):
|
def on_initialize_smart_object(self):
|
||||||
|
self.presence_simulation_slots_active = set()
|
||||||
|
|
||||||
# light_brightness_pct_list : ordered list of (Evaluator, label) pairs
|
# light_brightness_pct_list : ordered list of (Evaluator, label) pairs
|
||||||
# light_brightness_pct : currently active brightness % (str label)
|
# light_brightness_pct : currently active brightness % (str label)
|
||||||
self.light_brightness_pct_list = list()
|
self.light_brightness_pct_list = list()
|
||||||
@@ -196,6 +198,15 @@ class SmartLight(SmartSwitch):
|
|||||||
# SmartSwitch overrides
|
# SmartSwitch overrides
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
# While simulation is active for this light, ignore auto-off transitions
|
||||||
|
# from smart_conditions to keep deterministic on-duration windows.
|
||||||
|
def on_smart_conditions_change(self,prev_result,result):
|
||||||
|
if len(self.presence_simulation_slots_active) > 0:
|
||||||
|
if result == SmartCondition.Result.Succeeded and self.is_off():
|
||||||
|
self.switch_on()
|
||||||
|
return
|
||||||
|
super().on_smart_conditions_change(prev_result,result)
|
||||||
|
|
||||||
# Override: apply light_brightness_pct when turning on, if one is active.
|
# Override: apply light_brightness_pct when turning on, if one is active.
|
||||||
def switch_on(self):
|
def switch_on(self):
|
||||||
if self.light_brightness_pct != None:
|
if self.light_brightness_pct != None:
|
||||||
@@ -225,4 +236,61 @@ class SmartLight(SmartSwitch):
|
|||||||
self.log_info("Turning off")
|
self.log_info("Turning off")
|
||||||
self.call_service("light/turn_off", entity_id = self.entity_id)
|
self.call_service("light/turn_off", entity_id = self.entity_id)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Presence simulation API (called by HomeAlarm)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def start_presence_simulation(self, source_app=None, slot_name=None, payload=None):
|
||||||
|
if slot_name:
|
||||||
|
self.presence_simulation_slots_active.add(slot_name)
|
||||||
|
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
payload = {}
|
||||||
|
|
||||||
|
brightness_pct = payload.get('brightness_pct')
|
||||||
|
transition = payload.get('transition')
|
||||||
|
|
||||||
|
if brightness_pct is None and transition is None:
|
||||||
|
self.switch_on()
|
||||||
|
return
|
||||||
|
|
||||||
|
service_kwargs = {'entity_id': self.entity_id}
|
||||||
|
if brightness_pct is not None:
|
||||||
|
service_kwargs['brightness_pct'] = brightness_pct
|
||||||
|
if transition is not None:
|
||||||
|
service_kwargs['transition'] = transition
|
||||||
|
|
||||||
|
self.call_service("light/turn_on", **service_kwargs)
|
||||||
|
|
||||||
|
def stop_presence_simulation(self, source_app=None, slot_name=None, payload=None):
|
||||||
|
if slot_name and slot_name in self.presence_simulation_slots_active:
|
||||||
|
self.presence_simulation_slots_active.remove(slot_name)
|
||||||
|
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
payload = {}
|
||||||
|
|
||||||
|
if payload.get('keep_on', False):
|
||||||
|
return
|
||||||
|
|
||||||
|
transition = payload.get('transition')
|
||||||
|
if transition is None:
|
||||||
|
self.switch_off()
|
||||||
|
else:
|
||||||
|
self.call_service(
|
||||||
|
"light/turn_off",
|
||||||
|
entity_id=self.entity_id,
|
||||||
|
transition=transition,
|
||||||
|
)
|
||||||
|
|
||||||
|
# If no simulation slot is still active, re-apply current automation
|
||||||
|
# state so the light immediately converges to the expected result.
|
||||||
|
if len(self.presence_simulation_slots_active) == 0 and self.smart_conditions_evaluator:
|
||||||
|
result = self.smart_conditions_evaluator.evaluate(False)
|
||||||
|
if result == SmartCondition.Result.Succeeded:
|
||||||
|
if self.is_off():
|
||||||
|
self.switch_on()
|
||||||
|
else:
|
||||||
|
if self.is_on():
|
||||||
|
self.switch_off()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user