Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cbd6c4d
Create power_meter_shelly_em_pro
famascl3m Dec 20, 2025
6c43c82
Rename power_meter_shelly_em_pro to power_meter_shelly_em_pro.yaml
famascl3m Dec 20, 2025
f1b421b
Update power_meter_shelly_em_pro.yaml
famascl3m Dec 20, 2025
7d731f4
Update power_meter_shelly_em_pro.yaml
famascl3m Dec 20, 2025
ddbc8a8
Update power_meter_shelly_em_pro.yaml
famascl3m Dec 20, 2025
96bfdb4
Update power_meter_shelly_em_pro.yaml
famascl3m Dec 20, 2025
db9f5fd
Create scheduler_forced_run_safe.yaml
famascl3m Dec 21, 2025
158476d
Update scheduler_forced_run_safe.yaml
famascl3m Dec 21, 2025
b25b59a
Update scheduler_forced_run_safe.yaml
famascl3m Dec 21, 2025
e55656a
Update scheduler_forced_run_safe.yaml
famascl3m Dec 21, 2025
210e700
Update scheduler_forced_run_safe.yaml
famascl3m Dec 21, 2025
876a8ee
Create scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
02ad679
Delete solar_router/scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
5832a85
Create scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
d82c79b
Update scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
e60b39a
Update scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
930962f
Update scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
1fb6aba
Update scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
e6db24d
Update scheduler_forced_run_safe_beta.yaml
famascl3m Dec 21, 2025
979eefb
Create scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
10135d0
Create scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
85f07f8
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
2cb9e30
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
f775a2e
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
2813bc6
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
e063c11
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
1713ac6
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
2944764
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
974f811
Update scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 21, 2025
2596308
Delete solar_router/scheduler_forced_run_temperature_guard.yaml
famascl3m Dec 22, 2025
b06f9e0
Delete solar_router/scheduler_forced_run_safe_beta.yaml
famascl3m Dec 22, 2025
44ca787
Delete solar_router/scheduler_forced_run_safe.yaml
famascl3m Dec 22, 2025
e3acc74
Create scheduler_forced_run_tempo.yaml
famascl3m Dec 22, 2025
e54c075
Update scheduler_forced_run_tempo.yaml
famascl3m Dec 22, 2025
13d2c97
Create regulator_solid_state_relay_dev.yaml
famascl3m Jan 30, 2026
4940412
Delete solar_router/solar_router directory
famascl3m Jan 30, 2026
e7f463c
Create regulator_solid_state_relay_dev.yaml
famascl3m Jan 30, 2026
4127719
Update regulator_solid_state_relay_dev.yaml
famascl3m Jan 30, 2026
c2185fa
Create power_meter_shelly_em_pro_tri.yaml
famascl3m Feb 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions scheduler_forced_run_temperature_guard.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# Stop Forced Scheduler when temperature is above stop_temperature
# or when Safety limit reached is active
# Check is done every minute, ONLY during scheduler time window

substitutions:
scheduler_unique_id: "Forced"
custom_script: ${scheduler_unique_id}_fake_script

script:
# A fake empty script to run if user don't provide a custom one
- id: ${scheduler_unique_id}_fake_script
then:

# === TEMPERATURE / SAFETY GUARD ===
- id: ${scheduler_unique_id}_temperature_guard
mode: single
then:
- lambda: |-
auto now = id(${scheduler_unique_id}_sntp).now();
if (!now.is_valid()) return;

int now_min = now.hour * 60 + now.minute;
int begin_min =
id(${scheduler_unique_id}_scheduler_begin_hour).state * 60 +
id(${scheduler_unique_id}_scheduler_begin_min).state;
int end_min =
id(${scheduler_unique_id}_scheduler_end_hour).state * 60 +
id(${scheduler_unique_id}_scheduler_end_min).state;

bool in_scheduler_window;
if (begin_min <= end_min) {
in_scheduler_window = (now_min >= begin_min && now_min < end_min);
} else {
in_scheduler_window = (now_min >= begin_min || now_min < end_min);
}

if (!in_scheduler_window) return;

bool temp_reached =
!isnan(id(safety_temperature).state) &&
id(safety_temperature).state >= id(stop_temperature).state;

if (id(safety_limit) || temp_reached) {
ESP_LOGW("forced_scheduler",
"Stopping Forced Scheduler (temp=%.1f / safety=%d)",
id(safety_temperature).state,
id(safety_limit)
);
id(${scheduler_unique_id}_scheduler_activate).turn_off();
}

switch:
# Define if scheduler is active or not
- platform: template
name: "Activate ${scheduler_unique_id} Scheduler"
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
id: "${scheduler_unique_id}_scheduler_activate"
on_turn_off:
then:
- if:
condition:
- switch.is_off: activate
then:
- number.set:
id: router_level
value: 0
- switch.turn_on: activate

number:
# Scheduler Router level from 0 to 100
- platform: template
name: "${scheduler_unique_id} Scheduler Router Level"
id: "${scheduler_unique_id}_scheduler_router_level"
min_value: 0
max_value: 100
initial_value: 100
step: 1
unit_of_measurement: "%"
optimistic: true
mode: slider

- platform: template
name: "${scheduler_unique_id} Scheduler Checking End Threshold"
id: "${scheduler_unique_id}_scheduler_checking_end_threshold"
optimistic: true
min_value: 0
max_value: 720
step: 5
mode: box
initial_value: 5

- platform: template
name: "${scheduler_unique_id} Scheduler Begin Minute"
id: "${scheduler_unique_id}_scheduler_begin_min"
optimistic: true
min_value: 0
max_value: 55
step: 5
mode: box
initial_value: 0

- platform: template
name: "${scheduler_unique_id} Scheduler End Minute"
id: "${scheduler_unique_id}_scheduler_end_min"
optimistic: true
min_value: 0
max_value: 55
step: 5
mode: box
initial_value: 0

- platform: template
name: "${scheduler_unique_id} Scheduler Begin Hour"
id: "${scheduler_unique_id}_scheduler_begin_hour"
optimistic: true
min_value: 0
max_value: 23
step: 1
mode: box
initial_value: 22

- platform: template
name: "${scheduler_unique_id} Scheduler End Hour"
id: "${scheduler_unique_id}_scheduler_end_hour"
optimistic: true
min_value: 0
max_value: 23
step: 1
mode: box
initial_value: 6

time:
- platform: sntp
id: ${scheduler_unique_id}_sntp
on_time:
# === Every 1 minute: safety temperature guard ===
- seconds: 0
minutes: /1
then:
- script.execute: ${scheduler_unique_id}_temperature_guard

# === Existing scheduler logic (UNCHANGED) ===
- seconds: 0
minutes: /5
then:
- if:
condition:
- switch.is_on: ${scheduler_unique_id}_scheduler_activate
then:
- if:
condition:
lambda: |-
int beginTotalMinutes =
id(${scheduler_unique_id}_scheduler_begin_hour).state * 60 +
id(${scheduler_unique_id}_scheduler_begin_min).state;
int endTotalMinutes =
id(${scheduler_unique_id}_scheduler_end_hour).state * 60 +
id(${scheduler_unique_id}_scheduler_end_min).state;
int checkTotalMinutes =
id(${scheduler_unique_id}_sntp).now().hour * 60 +
id(${scheduler_unique_id}_sntp).now().minute;
if (beginTotalMinutes <= endTotalMinutes) {
return checkTotalMinutes >= beginTotalMinutes &&
checkTotalMinutes < endTotalMinutes;
} else {
return checkTotalMinutes >= beginTotalMinutes ||
checkTotalMinutes < endTotalMinutes;
}
then:
- switch.turn_off: activate
- number.set:
id: router_level
value: !lambda return id(${scheduler_unique_id}_scheduler_router_level).state;
- script.execute: ${custom_script}
else:
- if:
condition:
and:
- switch.is_off: activate
- lambda: |-
if (
id(${scheduler_unique_id}_scheduler_end_hour).state ==
id(${scheduler_unique_id}_sntp).now().hour &&
id(${scheduler_unique_id}_scheduler_end_min).state ==
id(${scheduler_unique_id}_sntp).now().minute
) {
return true;
}
int beginTotalMinutes =
id(${scheduler_unique_id}_scheduler_end_hour).state * 60 +
id(${scheduler_unique_id}_scheduler_end_min).state;
int endTotalMinutes =
beginTotalMinutes +
id(${scheduler_unique_id}_scheduler_checking_end_threshold).state;
int checkTotalMinutes =
id(${scheduler_unique_id}_sntp).now().hour * 60 +
id(${scheduler_unique_id}_sntp).now().minute;
if (beginTotalMinutes <= endTotalMinutes) {
return checkTotalMinutes >= beginTotalMinutes &&
checkTotalMinutes <= endTotalMinutes;
} else {
return checkTotalMinutes >= beginTotalMinutes ||
checkTotalMinutes <= endTotalMinutes;
}
then:
- number.set:
id: router_level
value: 0
- switch.turn_on: activate
68 changes: 68 additions & 0 deletions solar_router/power_meter_shelly_em_pro.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<<: !include power_meter_common.yaml

esphome:
min_version: 2025.5.0

http_request:
id: http_request_data
useragent: esphome/device
timeout: 10s
verify_ssl: false

script:
- id: power_meter_source
mode: single
then:
- if:
condition:
lambda: return network::is_connected();
then:
- http_request.get:
url: http://${power_meter_ip_address}/rpc/Shelly.GetStatus
request_headers:
Content-Type: application/json
Authorization: ${power_meter_auth_header}
capture_response: true
max_response_buffer_size: 4096
on_response:
then:
- lambda: |-
if (response->status_code != 200) {
ESP_LOGW("shelly", "HTTP error %d", response->status_code);
id(real_power).publish_state(NAN);
return;
}

bool ok = json::parse_json(body, [](JsonObject root) -> bool {
char key[8];
snprintf(key, sizeof(key), "em1:%d", ${emeter_index});

if (!root[key]["act_power"].is<float>()) {
ESP_LOGW("shelly", "act_power missing for %s", key);
return false;
}

float power = root[key]["act_power"].as<float>();
id(real_power).publish_state(power);
return true;
});

if (!ok) {
id(real_power).publish_state(NAN);
}
on_error:
then:
- lambda: |-
ESP_LOGW("shelly", "HTTP request failed");
id(real_power).publish_state(NAN);

time:
- platform: sntp
on_time:
- seconds: /1
then:
- if:
condition:
lambda: return id(power_meter_activated) != 0;
then:
- script.execute: power_meter_source
63 changes: 63 additions & 0 deletions solar_router/power_meter_shelly_em_pro_tri.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<<: !include power_meter_common.yaml

esphome:
min_version: 2025.5.0

http_request:
id: http_request_data
useragent: esphome/device
timeout: 10s
verify_ssl: false

script:
- id: power_meter_source
mode: single
then:
- if:
condition:
lambda: return network::is_connected();
then:
- http_request.get:
url: http://${power_meter_ip_address}/rpc/Shelly.GetStatus
request_headers:
Content-Type: application/json
Authorization: ${power_meter_auth_header}
capture_response: true
max_response_buffer_size: 4096
on_response:
then:
- lambda: |-
if (response->status_code != 200) {
ESP_LOGW("shelly", "HTTP error %d", response->status_code);
id(real_power).publish_state(NAN);
return;
}
bool ok = json::parse_json(body, [](JsonObject root) -> bool {
// Pour Shelly EM Pro tri, la clé est "em:0" au lieu de "em1:X"
if (!root["em:0"]["total_act_power"].is<float>()) {
ESP_LOGW("shelly", "total_act_power missing for em:0");
return false;
}
float power = root["em:0"]["total_act_power"].as<float>();
id(real_power).publish_state(power);
return true;
});
if (!ok) {
id(real_power).publish_state(NAN);
}
on_error:
then:
- lambda: |-
ESP_LOGW("shelly", "HTTP request failed");
id(real_power).publish_state(NAN);

time:
- platform: sntp
on_time:
- seconds: /1
then:
- if:
condition:
lambda: return id(power_meter_activated) != 0;
then:
- script.execute: power_meter_source
25 changes: 25 additions & 0 deletions solar_router/regulator_solid_state_relay_dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# ----------------------------------------------------------------------------------------------------
# Define scripts for energy divertion
# ----------------------------------------------------------------------------------------------------

script:
# Apply regulation on relay
- id: regulation_control
mode: single
then:
# Apply opening level on relay ldec output
- output.turn_on: ssr_output
- output.set_level:
id: ssr_output
level: !lambda return id(regulator_opening).state/100.0;

# ----------------------------------------------------------------------------------------------------
# relay control
# ----------------------------------------------------------------------------------------------------

# Control the relay through GPIO
output:
- platform: slow_pwm
id: ssr_output
pin: ${regulator_gate_pin}
period: 100ms
Loading
Loading