<?php
namespace ControlScripts\Scopes;
use \SHServ\Middleware\ControlScripts;
use \SHServ\Implements\ControlScriptsInterface;
/**
* Sleep mode: turns off all indoor lighting, blocks physical buttons (mute),
* and guards against web-client toggles via regular script.
* Outdoor spotlights are left untouched.
*/
class SleepScope extends ControlScripts implements ControlScriptsInterface {
use \ControlScripts\Common;
/** Indoor relays that must stay OFF while sleep is active. */
protected const INDOOR_RELAYS = [
"light_hub_1:0",
"light_hub_1:1",
"light_hub_1:2",
"craft_table_lamp",
"computer_table_lamp",
"bathroom_2_light",
"plants_room_light",
"italy_lamp_relay",
"floor_lamp_relay",
"server_room_light",
"fisrt_floor_big_relay:0",
"fisrt_floor_big_relay:1",
"fisrt_floor_big_relay:2",
"fisrt_floor_big_relay:3",
"fisrt_floor_big_relay:4",
];
public function register_sync_map(): void {
$this -> register_global_device_sync_map();
}
public function register_events_handlers(): void {
// No per-button event handlers needed; we mute channels in activate_sleep.
}
public function register_actions_scripts(): void {
$this -> add_action_script([
"alias" => "activate_sleep",
"name" => "Режим сна",
"description" => "Включить режим сна: выключить всё indoor-освещение и заблокировать кнопки",
"author" => "Eugene Sukhodolskiy",
"icon" => '<i class="ph ph-moon"></i>',
"danger_level" => "cautious",
"state_callback" => function() {
$isSleep = $this -> mode() -> is('sleep');
return [
["label" => $isSleep ? "On" : "Off", "variant" => $isSleep ? "success" : "secondary"]
];
},
], function($params) {
$this -> mode() -> enable('sleep');
// 1. Turn everything off immediately
$results = $this -> helper() -> group_set_state(self::INDOOR_RELAYS, false, $this -> sync_map());
// 2. Mute indoor button channels so physical presses are ignored
$this -> set_indoor_buttons_mute(true);
return [
"mode_enabled" => true,
"lights_turned_off" => $results,
];
});
$this -> add_action_script([
"alias" => "deactivate_sleep",
"name" => "Выход из сна",
"description" => "Отключить режим сна и разблокировать кнопки",
"author" => "Eugene Sukhodolskiy",
"icon" => '<i class="ph ph-sun"></i>',
"danger_level" => "safe",
"state_callback" => function() {
$isSleep = $this -> mode() -> is('sleep');
return [
["label" => $isSleep ? "On" : "Off", "variant" => $isSleep ? "success" : "secondary"]
];
},
], function($params) {
$this -> mode() -> disable('sleep');
// Unmute buttons and sync their indicators back to relay states
$this -> set_indoor_buttons_mute(false);
return [
"mode_disabled" => true,
];
});
}
public function register_regular_scripts(): void {
// Guard script: if sleep mode is active and someone turned a light on via
// web client (or any other way), turn it back off immediately.
$this -> add_regular_script([
"alias" => "sleep_guard",
"name" => "Sleep guard",
"description" => "Принудительно гасит indoor-свет, пока активен режим сна",
"author" => "Eugene Sukhodolskiy",
], function() {
if (!$this -> mode() -> is('sleep')) {
return;
}
foreach (self::INDOOR_RELAYS as $target) {
$parts = explode(":", $target, 2);
$alias = $parts[0];
$channel = isset($parts[1]) ? intval($parts[1]) : null;
$relay = $this -> devices() -> by_alias($alias);
if (!$relay) {
continue;
}
$relay_api = $relay -> device_api();
if (!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) {
continue;
}
$status = $relay_api -> get_status();
$ch = $channel ?? 0;
$state = $status["channels"][$ch]["state"] ?? "off";
if ($state === "on") {
if ($channel === null) {
$relay_api -> set_state(false);
} else {
$relay_api -> set_channel_state(false, $channel);
}
$this -> helper() -> sync_relay_to_btns($this -> sync_map(), $alias);
}
}
});
}
/**
* Mute or unmute button channels that are wired to INDOOR relays.
* When muted, physical button presses on the device are ignored.
* When unmuted, we restore enabled/disabled state from the linked relay.
*/
protected function set_indoor_buttons_mute(bool $mute): void {
$indoor_map = [];
foreach (self::INDOOR_RELAYS as $target) {
$parts = explode(":", $target, 2);
$indoor_map[$parts[0]] = isset($parts[1]) ? intval($parts[1]) : null;
}
$sync_map = $this -> sync_map();
if (!isset($sync_map["connections"])) {
return;
}
foreach ($sync_map["connections"] as $connection) {
$has_indoor = false;
$btn_entries = [];
foreach ($connection as $entry) {
$type = $entry["type"] ?? "";
$alias = $entry["alias"] ?? "";
$channel = $entry["channel"] ?? 0;
if ($type === "relay" && isset($indoor_map[$alias])) {
$expected = $indoor_map[$alias];
if ($expected === null || $expected === $channel) {
$has_indoor = true;
}
} elseif ($type === "button") {
$btn_entries[] = $entry;
}
}
if (!$has_indoor || empty($btn_entries)) {
continue;
}
foreach ($btn_entries as $btn) {
$btn_device = $this -> devices() -> by_alias($btn["alias"]);
if (!$btn_device) {
continue;
}
$btn_api = $btn_device -> device_api();
if (!($btn_api instanceof \SHServ\Tools\DeviceAPI\Button)) {
continue;
}
$btn_channel = $btn["channel"] ?? 0;
if ($mute) {
$btn_api -> set_channel_state("mute", $btn_channel);
} else {
// Unmute: figure out whether the linked relay is on or off
$sync_state = "disabled";
foreach ($connection as $e) {
if (($e["type"] ?? "") === "relay" && isset($indoor_map[$e["alias"]])) {
$expected = $indoor_map[$e["alias"]];
$relay_ch = $e["channel"] ?? 0;
if ($expected === null || $expected === $relay_ch) {
$relay = $this -> devices() -> by_alias($e["alias"]);
if ($relay) {
$relay_api = $relay -> device_api();
if ($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) {
$status = $relay_api -> get_status();
$st = $status["channels"][$relay_ch]["state"] ?? "off";
$sync_state = ($st === "on") ? "enabled" : "disabled";
}
}
break;
}
}
}
$btn_api -> set_channel_state($sync_state, $btn_channel);
}
}
}
}
}