diff --git a/automation/Common.php b/automation/Common.php new file mode 100644 index 0000000..e1e6b60 --- /dev/null +++ b/automation/Common.php @@ -0,0 +1,60 @@ + add_sync_connection($connection); + } + } + + public function btn_on_online(String $alias, Array $muted = []): void { + $this -> add_event_handler("button@{$alias}.online", function(Device $btns_block, Array $data) use ($muted) { + $btns_block_api = $btns_block -> device_api(); + if($btns_block_api instanceof \SHServ\Tools\DeviceAPI\Button) { + foreach($muted as $ch) { + $btns_block_api -> set_channel_state("mute", $ch); + } + } + + $this -> helper() -> sync_btn_channels($this -> sync_map(), $btns_block -> alias); + }); + } + + public function set_btns_click_handlers($alias): void { + $self = $this; + $buttons = $this -> helper() -> prepare_sync_map_by_alias($this -> sync_map(), $alias); + + foreach($buttons as $btn_channel => $entry) { + if($entry[0]["type"] != "relay") { + continue; + } + + $relay_alias = $entry[0]["alias"]; + $relay_channel = $entry[0]["channel"]; + + $this -> add_event_handler("button@{$alias}({$btn_channel}).press", function(Device $btns_block, Array $data) use ($self, $btn_channel, $relay_alias, $relay_channel) { + $btns_block_api = $btns_block -> device_api(); + $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); + + if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay and $btns_block_api instanceof \SHServ\Tools\DeviceAPI\Button) { + $relay_api -> toggle_channel($relay_channel); + $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); + } + }); + } + } +} diff --git a/automation/Scopes/LightHubScope.php b/automation/Scopes/LightHubScope.php new file mode 100644 index 0000000..de9da3b --- /dev/null +++ b/automation/Scopes/LightHubScope.php @@ -0,0 +1,164 @@ + register_global_device_sync_map(); + } + + public function register_events_handlers(): void { + $this -> btn_on_online("master_room_btns", [2, 3]); + $this -> btn_on_online("btns_hall2_1"); + $this -> btn_on_online("hall_secondary"); + $this -> btn_on_online("bed_btns_right_1"); + $this -> btn_on_online("bed_btns_left"); + $this -> btn_on_online("kitchen_buttons_1"); + $this -> btn_on_online("kitchen_buttons_2"); + $this -> btn_on_online("first_hall_buttons1", [3]); + $this -> btn_on_online("first_hall_buttons2"); + $this -> set_btns_click_handlers("master_room_btns"); + $this -> set_btns_click_handlers("btns_hall2_1"); + $this -> set_btns_click_handlers("hall_secondary"); + $this -> set_btns_click_handlers("bed_btns_right_1"); + $this -> set_btns_click_handlers("bed_btns_left"); + $this -> set_btns_click_handlers("kitchen_buttons_1"); + $this -> set_btns_click_handlers("kitchen_buttons_2"); + $this -> set_btns_click_handlers("first_hall_buttons1"); + $this -> set_btns_click_handlers("first_hall_buttons2"); + + + + + $this -> btn_on_online("plants_room_btns", [1]); + $this -> set_btns_click_handlers("plants_room_btns"); + + } + + public function register_actions_scripts(): void { + $this -> add_action_script([ + "alias" => "master_room_lamp_switcher", + "name" => "Осн. свет в спальне", + "icon" => '', + "description" => "Включить/выключить основной свет в спальне", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> lamp_switch("light_hub_1", 1); + }); + + $this -> add_action_script([ + "alias" => "hallway2_lamp_switcher", + "name" => "Осн. свет в холе, эт2", + "icon" => '', + "description" => "Включить/выключить основной свет в холе на втором этаже", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> lamp_switch("light_hub_1", 2); + }); + + $this -> add_action_script([ + "alias" => "bathroom2_lamp_switcher", + "name" => "Осн. свет в ванной 2", + "icon" => '', + "description" => "Включить/выключить основной свет в ванной комнате на верху", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> lamp_switch("light_hub_1", 3); + }); + + // --- + + $this -> add_action_script([ + "alias" => "hall1_light_switcher", + "name" => "Свет в прихожей", + "icon" => '', + "description" => "Включить/выключить свет в прихожей", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return + $this -> lamp_switch("fisrt_floor_big_relay", 3) and $this -> lamp_switch("fisrt_floor_big_relay", 4); + }); + + $this -> add_action_script([ + "alias" => "kitchen_light_switcher", + "name" => "Полный свет на кухне", + "icon" => '', + "description" => "Включить/выключить весь имеющийся свет на кухне", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return + $this -> lamp_switch("fisrt_floor_big_relay", 0) and $this -> lamp_switch("fisrt_floor_big_relay", 1); + }); + + $this -> add_action_script([ + "alias" => "hatch_open", + "name" => "Открыть люк", + "icon" => '', + "description" => "Открыть люк на кухне", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return + $this -> hatch_open("kitchen_hatch", 100); + }); + + $this -> add_action_script([ + "alias" => "hatch_close", + "name" => "Закрыть люк", + "icon" => '', + "description" => "Закрыть люк на кухне", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return + $this -> hatch_close("kitchen_hatch", 100); + }); + } + + public function register_regular_scripts(): void { + } + + // ACTIONS + + protected function lamp_switch(String $relay_alias, int $channel): Array { + $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); + + $result = false; + if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { + $result = $relay_api -> toggle_channel($channel); + $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); + } + + return [ + "result" => $result + ]; + } + + protected function hatch_do(String $hatch_alias, String $operation = "open", int $percent = 100): Array | bool { + $hatch_api = $this -> devices() -> by_alias($hatch_alias) -> device_api(); + + $result = false; + if($hatch_api instanceof \SHServ\Tools\DeviceAPI\Hatch) { + switch($operation) { + case "open": $result = $hatch_api -> open($percent); + break; + case "close": $result = $hatch_api -> close($percent); + break; + } + } + + return $result; + } + + protected function hatch_open(String $hatch_alias, int $percent = 100) { + return $this -> hatch_do($hatch_alias, "open", $percent); + } + + protected function hatch_close(String $hatch_alias, int $percent = 100) { + return $this -> hatch_do($hatch_alias, "close", $percent); + } + +} diff --git a/automation/Scopes/OfficeRoomScope.php b/automation/Scopes/OfficeRoomScope.php new file mode 100644 index 0000000..8d6d36a --- /dev/null +++ b/automation/Scopes/OfficeRoomScope.php @@ -0,0 +1,71 @@ + register_global_device_sync_map(); + } + + public function register_events_handlers(): void { + $this -> btn_on_online("buttons_office_room"); + $this -> set_btns_click_handlers("buttons_office_room"); + } + + public function register_actions_scripts(): void { + $this -> add_action_script([ + "alias" => "computer_table_lamp_switch", + "name" => "Комп. лампа", + "icon" => '', + "description" => "Вкл/Выкл. настольную компьютерную лампу", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> lamp_switch("computer_table_lamp", 0); + }); + + $this -> add_action_script([ + "alias" => "craft_table_lamp_switch", + "name" => "Крафт. лампа", + "icon" => '', + "description" => "Вкл/Выкл. настольную лампу на столе для крафта", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> lamp_switch("craft_table_lamp", 0); + }); + + $this -> add_action_script([ + "alias" => "main_lamps_switcher", + "name" => "Осн. свет в кабинете", + "icon" => '', + "description" => "Включить/выключить основной свет в кабинете", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> lamp_switch("light_hub_1", 0); + }); + } + + public function register_regular_scripts(): void { + } + + // ACTIONS + + protected function lamp_switch(String $relay_alias, int $channel): Array { + $btns_block_api = $this -> devices() -> by_alias("buttons_office_room") -> device_api(); + $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); + + $result = false; + if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { + $result = $relay_api -> toggle_channel($channel); + $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); + } + + return [ + "result" => $result + ]; + } + +} diff --git a/automation/Scopes/SpotlightsScope.php b/automation/Scopes/SpotlightsScope.php new file mode 100644 index 0000000..269c19d --- /dev/null +++ b/automation/Scopes/SpotlightsScope.php @@ -0,0 +1,117 @@ + register_global_device_sync_map(); + } + + public function register_events_handlers(): void { + $this -> set_btns_click_handlers("buttons_backdoor"); + $this -> set_btns_click_handlers("master_door_btns"); + $this -> btn_on_online("buttons_backdoor", [3]); + $this -> btn_on_online("master_door_btns", [2]); + } + + public function register_actions_scripts(): void { + $this -> add_action_script([ + "alias" => "spotlights_on", + "name" => "All Spotlights On", + "icon" => '', + "description" => "Включить все прожекторы", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> all_spotlight_switch(true); + }); + + $this -> add_action_script([ + "alias" => "spotlights_off", + "name" => "All Spotlights Off", + "icon" => '', + "description" => "Выключить все прожекторы", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> all_spotlight_switch(false); + }); + + $this -> add_action_script([ + "alias" => "front_spotlight_on", + "name" => "ВКЛ фронтальный прожектор", + "icon" => '', + "description" => "Включить только фронтальный прожектор на главном входе", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> spotlight_switch("spotlight_main_front_1", true); + }); + + $this -> add_action_script([ + "alias" => "front_spotlight_off", + "name" => "ВЫКЛ фронтальный прожектор", + "icon" => '', + "description" => "Выключить только фронтальный прожектор на главном входе", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + return $this -> spotlight_switch("spotlight_main_front_1", false); + }); + } + + public function register_regular_scripts(): void { + $this -> add_regular_script([ + "alias" => "spotlights_by_time", + "name" => "Spotlights by Time", + "description" => "Управление уличными прожекторами по времени", + "author" => "Eugene Sukhodolskiy" + ], function () { + }); + } + + // ACTIONS + + protected function spotlight_switch(String $relay_alias, Bool $state = false) { + $device_entries = $this -> helper() -> get_sync_entries_by_type($this -> sync_map(), "relay"); + + $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); + $result = false; + if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { + $result = $relay_api -> set_state($state); + $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); + } + + return [ + "result" => $result + ]; + } + + protected function all_spotlight_switch(Bool $state = false): Array { + $results = []; + $device_entries = $this -> helper() -> get_sync_entries_by_type($this -> sync_map(), "relay"); + + foreach($device_entries as $device_entry) { + $relay_api = $this -> devices() -> by_alias($device_entry["alias"]) -> device_api(); + $result = false; + $ch = $device_entry["channel"]; + + if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { + $status = $relay_api -> get_status(); + + if(isset($status["channels"][$ch]["state"]) and $status["channels"][$ch]["state"] != ($state ? "on" : "off")) { + $result = $relay_api -> set_state($state); + } + } + + $results[$device_entry["alias"]] = $result; + $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $device_entry["alias"]); + } + + + return [ + "devices" => $results + ]; + } + +} diff --git a/automation/Scopes/TestScriptsScope.php b/automation/Scopes/TestScriptsScope.php new file mode 100644 index 0000000..1fa3994 --- /dev/null +++ b/automation/Scopes/TestScriptsScope.php @@ -0,0 +1,33 @@ + add_action_script([ + "alias" => "stand_relay_toggle", + "icon" => '', + "name" => "Toggle Stang Relay", + "description" => "Управление тестовым стендом у меня на столе. Просто переключатель", + "author" => "Eugene Sukhodolskiy" + ], function($params) { + $device = $this -> devices() -> by_alias("test_stand_relay"); + return [ + "device_response" => $device -> device_api() -> toggle_channel(), + ]; + }); + } + + public function register_regular_scripts(): void { + } + +} \ No newline at end of file diff --git a/automation/scopes-manifest.json b/automation/scopes-manifest.json new file mode 100644 index 0000000..5f034fd --- /dev/null +++ b/automation/scopes-manifest.json @@ -0,0 +1,8 @@ +{ + "scopes": [ + "LightHubScope", + "OfficeRoomScope", + "SpotlightsScope", + "TestScriptsScope" + ] +} diff --git a/automation/sync-map.json b/automation/sync-map.json new file mode 100644 index 0000000..02e0879 --- /dev/null +++ b/automation/sync-map.json @@ -0,0 +1,88 @@ +[ + [ + {"type": "relay", "alias": "spotlight_main_back_1", "channel": 0}, + {"type": "button", "alias": "buttons_backdoor", "channel": 2}, + {"type": "button", "alias": "master_door_btns", "channel": 1}, + {"type": "button", "alias": "bed_btns_left", "channel": 0} + ], + [ + {"type": "relay", "alias": "spotlight_main_back_2", "channel": 0}, + {"type": "button", "alias": "buttons_backdoor", "channel": 1}, + {"type": "button", "alias": "master_door_btns", "channel": 0} + ], + [ + {"type": "relay", "alias": "spotlight_main_front_1", "channel": 0}, + {"type": "button", "alias": "buttons_backdoor", "channel": 0}, + {"type": "button", "alias": "master_door_btns", "channel": 3} + ], + [ + {"type": "relay", "alias": "light_hub_1", "channel": 0}, + {"type": "button", "alias": "buttons_office_room", "channel": 3} + ], + [ + {"type": "relay", "alias": "craft_table_lamp", "channel": 0}, + {"type": "button", "alias": "buttons_office_room", "channel": 2} + ], + [ + {"type": "relay", "alias": "computer_table_lamp", "channel": 0}, + {"type": "button", "alias": "buttons_office_room", "channel": 1} + ], + [ + {"type": "relay", "alias": "light_hub_1", "channel": 2}, + {"type": "button", "alias": "buttons_office_room", "channel": 0}, + {"type": "button", "alias": "master_room_btns", "channel": 1}, + {"type": "button", "alias": "btns_hall2_1", "channel": 0}, + {"type": "button", "alias": "hall_secondary", "channel": 0}, + {"type": "button", "alias": "bed_btns_right_1", "channel": 0} + ], + [ + {"type": "relay", "alias": "light_hub_1", "channel": 1}, + {"type": "button", "alias": "master_room_btns", "channel": 0}, + {"type": "button", "alias": "bed_btns_right_1", "channel": 1}, + {"type": "button", "alias": "bed_btns_left", "channel": 1} + ], + [ + {"type": "relay", "alias": "bathroom_2_light", "channel": 0}, + {"type": "button", "alias": "hall_secondary", "channel": 1} + ], + [ + {"type": "relay", "alias": "plants_room_light", "channel": 0}, + {"type": "button", "alias": "plants_room_btns", "channel": 3} + ], + [ + {"type": "relay", "alias": "italy_lamp_relay", "channel": 0}, + {"type": "button", "alias": "plants_room_btns", "channel": 2} + ], + [ + {"type": "relay", "alias": "floor_lamp_relay", "channel": 0}, + {"type": "button", "alias": "plants_room_btns", "channel": 0} + ], + [ + {"type": "relay", "alias": "server_room_light", "channel": 0}, + {"type": "button", "alias": "btns_hall2_1", "channel": 1} + ], + [ + {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 0}, + {"type": "button", "alias": "kitchen_buttons_1", "channel": 0}, + {"type": "button", "alias": "kitchen_buttons_2", "channel": 0} + ], + [ + {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 1}, + {"type": "button", "alias": "kitchen_buttons_1", "channel": 1}, + {"type": "button", "alias": "kitchen_buttons_2", "channel": 1} + ], + [ + {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 2}, + {"type": "button", "alias": "first_hall_buttons1", "channel": 0} + ], + [ + {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 3}, + {"type": "button", "alias": "first_hall_buttons1", "channel": 1}, + {"type": "button", "alias": "first_hall_buttons2", "channel": 0} + ], + [ + {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 4}, + {"type": "button", "alias": "first_hall_buttons1", "channel": 2}, + {"type": "button", "alias": "first_hall_buttons2", "channel": 1} + ] +] \ No newline at end of file diff --git a/server/ControlScripts/Common.php b/server/ControlScripts/Common.php deleted file mode 100644 index e1e6b60..0000000 --- a/server/ControlScripts/Common.php +++ /dev/null @@ -1,60 +0,0 @@ - add_sync_connection($connection); - } - } - - public function btn_on_online(String $alias, Array $muted = []): void { - $this -> add_event_handler("button@{$alias}.online", function(Device $btns_block, Array $data) use ($muted) { - $btns_block_api = $btns_block -> device_api(); - if($btns_block_api instanceof \SHServ\Tools\DeviceAPI\Button) { - foreach($muted as $ch) { - $btns_block_api -> set_channel_state("mute", $ch); - } - } - - $this -> helper() -> sync_btn_channels($this -> sync_map(), $btns_block -> alias); - }); - } - - public function set_btns_click_handlers($alias): void { - $self = $this; - $buttons = $this -> helper() -> prepare_sync_map_by_alias($this -> sync_map(), $alias); - - foreach($buttons as $btn_channel => $entry) { - if($entry[0]["type"] != "relay") { - continue; - } - - $relay_alias = $entry[0]["alias"]; - $relay_channel = $entry[0]["channel"]; - - $this -> add_event_handler("button@{$alias}({$btn_channel}).press", function(Device $btns_block, Array $data) use ($self, $btn_channel, $relay_alias, $relay_channel) { - $btns_block_api = $btns_block -> device_api(); - $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); - - if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay and $btns_block_api instanceof \SHServ\Tools\DeviceAPI\Button) { - $relay_api -> toggle_channel($relay_channel); - $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); - } - }); - } - } -} diff --git a/server/ControlScripts/Scopes/LightHubScope.php b/server/ControlScripts/Scopes/LightHubScope.php deleted file mode 100644 index de9da3b..0000000 --- a/server/ControlScripts/Scopes/LightHubScope.php +++ /dev/null @@ -1,164 +0,0 @@ - register_global_device_sync_map(); - } - - public function register_events_handlers(): void { - $this -> btn_on_online("master_room_btns", [2, 3]); - $this -> btn_on_online("btns_hall2_1"); - $this -> btn_on_online("hall_secondary"); - $this -> btn_on_online("bed_btns_right_1"); - $this -> btn_on_online("bed_btns_left"); - $this -> btn_on_online("kitchen_buttons_1"); - $this -> btn_on_online("kitchen_buttons_2"); - $this -> btn_on_online("first_hall_buttons1", [3]); - $this -> btn_on_online("first_hall_buttons2"); - $this -> set_btns_click_handlers("master_room_btns"); - $this -> set_btns_click_handlers("btns_hall2_1"); - $this -> set_btns_click_handlers("hall_secondary"); - $this -> set_btns_click_handlers("bed_btns_right_1"); - $this -> set_btns_click_handlers("bed_btns_left"); - $this -> set_btns_click_handlers("kitchen_buttons_1"); - $this -> set_btns_click_handlers("kitchen_buttons_2"); - $this -> set_btns_click_handlers("first_hall_buttons1"); - $this -> set_btns_click_handlers("first_hall_buttons2"); - - - - - $this -> btn_on_online("plants_room_btns", [1]); - $this -> set_btns_click_handlers("plants_room_btns"); - - } - - public function register_actions_scripts(): void { - $this -> add_action_script([ - "alias" => "master_room_lamp_switcher", - "name" => "Осн. свет в спальне", - "icon" => '', - "description" => "Включить/выключить основной свет в спальне", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> lamp_switch("light_hub_1", 1); - }); - - $this -> add_action_script([ - "alias" => "hallway2_lamp_switcher", - "name" => "Осн. свет в холе, эт2", - "icon" => '', - "description" => "Включить/выключить основной свет в холе на втором этаже", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> lamp_switch("light_hub_1", 2); - }); - - $this -> add_action_script([ - "alias" => "bathroom2_lamp_switcher", - "name" => "Осн. свет в ванной 2", - "icon" => '', - "description" => "Включить/выключить основной свет в ванной комнате на верху", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> lamp_switch("light_hub_1", 3); - }); - - // --- - - $this -> add_action_script([ - "alias" => "hall1_light_switcher", - "name" => "Свет в прихожей", - "icon" => '', - "description" => "Включить/выключить свет в прихожей", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return - $this -> lamp_switch("fisrt_floor_big_relay", 3) and $this -> lamp_switch("fisrt_floor_big_relay", 4); - }); - - $this -> add_action_script([ - "alias" => "kitchen_light_switcher", - "name" => "Полный свет на кухне", - "icon" => '', - "description" => "Включить/выключить весь имеющийся свет на кухне", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return - $this -> lamp_switch("fisrt_floor_big_relay", 0) and $this -> lamp_switch("fisrt_floor_big_relay", 1); - }); - - $this -> add_action_script([ - "alias" => "hatch_open", - "name" => "Открыть люк", - "icon" => '', - "description" => "Открыть люк на кухне", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return - $this -> hatch_open("kitchen_hatch", 100); - }); - - $this -> add_action_script([ - "alias" => "hatch_close", - "name" => "Закрыть люк", - "icon" => '', - "description" => "Закрыть люк на кухне", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return - $this -> hatch_close("kitchen_hatch", 100); - }); - } - - public function register_regular_scripts(): void { - } - - // ACTIONS - - protected function lamp_switch(String $relay_alias, int $channel): Array { - $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); - - $result = false; - if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { - $result = $relay_api -> toggle_channel($channel); - $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); - } - - return [ - "result" => $result - ]; - } - - protected function hatch_do(String $hatch_alias, String $operation = "open", int $percent = 100): Array | bool { - $hatch_api = $this -> devices() -> by_alias($hatch_alias) -> device_api(); - - $result = false; - if($hatch_api instanceof \SHServ\Tools\DeviceAPI\Hatch) { - switch($operation) { - case "open": $result = $hatch_api -> open($percent); - break; - case "close": $result = $hatch_api -> close($percent); - break; - } - } - - return $result; - } - - protected function hatch_open(String $hatch_alias, int $percent = 100) { - return $this -> hatch_do($hatch_alias, "open", $percent); - } - - protected function hatch_close(String $hatch_alias, int $percent = 100) { - return $this -> hatch_do($hatch_alias, "close", $percent); - } - -} diff --git a/server/ControlScripts/Scopes/OfficeRoomScope.php b/server/ControlScripts/Scopes/OfficeRoomScope.php deleted file mode 100644 index 8d6d36a..0000000 --- a/server/ControlScripts/Scopes/OfficeRoomScope.php +++ /dev/null @@ -1,71 +0,0 @@ - register_global_device_sync_map(); - } - - public function register_events_handlers(): void { - $this -> btn_on_online("buttons_office_room"); - $this -> set_btns_click_handlers("buttons_office_room"); - } - - public function register_actions_scripts(): void { - $this -> add_action_script([ - "alias" => "computer_table_lamp_switch", - "name" => "Комп. лампа", - "icon" => '', - "description" => "Вкл/Выкл. настольную компьютерную лампу", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> lamp_switch("computer_table_lamp", 0); - }); - - $this -> add_action_script([ - "alias" => "craft_table_lamp_switch", - "name" => "Крафт. лампа", - "icon" => '', - "description" => "Вкл/Выкл. настольную лампу на столе для крафта", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> lamp_switch("craft_table_lamp", 0); - }); - - $this -> add_action_script([ - "alias" => "main_lamps_switcher", - "name" => "Осн. свет в кабинете", - "icon" => '', - "description" => "Включить/выключить основной свет в кабинете", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> lamp_switch("light_hub_1", 0); - }); - } - - public function register_regular_scripts(): void { - } - - // ACTIONS - - protected function lamp_switch(String $relay_alias, int $channel): Array { - $btns_block_api = $this -> devices() -> by_alias("buttons_office_room") -> device_api(); - $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); - - $result = false; - if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { - $result = $relay_api -> toggle_channel($channel); - $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); - } - - return [ - "result" => $result - ]; - } - -} diff --git a/server/ControlScripts/Scopes/SpotlightsScope.php b/server/ControlScripts/Scopes/SpotlightsScope.php deleted file mode 100644 index 269c19d..0000000 --- a/server/ControlScripts/Scopes/SpotlightsScope.php +++ /dev/null @@ -1,117 +0,0 @@ - register_global_device_sync_map(); - } - - public function register_events_handlers(): void { - $this -> set_btns_click_handlers("buttons_backdoor"); - $this -> set_btns_click_handlers("master_door_btns"); - $this -> btn_on_online("buttons_backdoor", [3]); - $this -> btn_on_online("master_door_btns", [2]); - } - - public function register_actions_scripts(): void { - $this -> add_action_script([ - "alias" => "spotlights_on", - "name" => "All Spotlights On", - "icon" => '', - "description" => "Включить все прожекторы", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> all_spotlight_switch(true); - }); - - $this -> add_action_script([ - "alias" => "spotlights_off", - "name" => "All Spotlights Off", - "icon" => '', - "description" => "Выключить все прожекторы", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> all_spotlight_switch(false); - }); - - $this -> add_action_script([ - "alias" => "front_spotlight_on", - "name" => "ВКЛ фронтальный прожектор", - "icon" => '', - "description" => "Включить только фронтальный прожектор на главном входе", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> spotlight_switch("spotlight_main_front_1", true); - }); - - $this -> add_action_script([ - "alias" => "front_spotlight_off", - "name" => "ВЫКЛ фронтальный прожектор", - "icon" => '', - "description" => "Выключить только фронтальный прожектор на главном входе", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - return $this -> spotlight_switch("spotlight_main_front_1", false); - }); - } - - public function register_regular_scripts(): void { - $this -> add_regular_script([ - "alias" => "spotlights_by_time", - "name" => "Spotlights by Time", - "description" => "Управление уличными прожекторами по времени", - "author" => "Eugene Sukhodolskiy" - ], function () { - }); - } - - // ACTIONS - - protected function spotlight_switch(String $relay_alias, Bool $state = false) { - $device_entries = $this -> helper() -> get_sync_entries_by_type($this -> sync_map(), "relay"); - - $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); - $result = false; - if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { - $result = $relay_api -> set_state($state); - $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $relay_alias); - } - - return [ - "result" => $result - ]; - } - - protected function all_spotlight_switch(Bool $state = false): Array { - $results = []; - $device_entries = $this -> helper() -> get_sync_entries_by_type($this -> sync_map(), "relay"); - - foreach($device_entries as $device_entry) { - $relay_api = $this -> devices() -> by_alias($device_entry["alias"]) -> device_api(); - $result = false; - $ch = $device_entry["channel"]; - - if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { - $status = $relay_api -> get_status(); - - if(isset($status["channels"][$ch]["state"]) and $status["channels"][$ch]["state"] != ($state ? "on" : "off")) { - $result = $relay_api -> set_state($state); - } - } - - $results[$device_entry["alias"]] = $result; - $this -> helper() -> sync_relay_to_btns($this -> sync_map(), $device_entry["alias"]); - } - - - return [ - "devices" => $results - ]; - } - -} diff --git a/server/ControlScripts/Scopes/TestScriptsScope.php b/server/ControlScripts/Scopes/TestScriptsScope.php deleted file mode 100644 index 1fa3994..0000000 --- a/server/ControlScripts/Scopes/TestScriptsScope.php +++ /dev/null @@ -1,33 +0,0 @@ - add_action_script([ - "alias" => "stand_relay_toggle", - "icon" => '', - "name" => "Toggle Stang Relay", - "description" => "Управление тестовым стендом у меня на столе. Просто переключатель", - "author" => "Eugene Sukhodolskiy" - ], function($params) { - $device = $this -> devices() -> by_alias("test_stand_relay"); - return [ - "device_response" => $device -> device_api() -> toggle_channel(), - ]; - }); - } - - public function register_regular_scripts(): void { - } - -} \ No newline at end of file diff --git a/server/ControlScripts/sync-map.json b/server/ControlScripts/sync-map.json deleted file mode 100644 index 02e0879..0000000 --- a/server/ControlScripts/sync-map.json +++ /dev/null @@ -1,88 +0,0 @@ -[ - [ - {"type": "relay", "alias": "spotlight_main_back_1", "channel": 0}, - {"type": "button", "alias": "buttons_backdoor", "channel": 2}, - {"type": "button", "alias": "master_door_btns", "channel": 1}, - {"type": "button", "alias": "bed_btns_left", "channel": 0} - ], - [ - {"type": "relay", "alias": "spotlight_main_back_2", "channel": 0}, - {"type": "button", "alias": "buttons_backdoor", "channel": 1}, - {"type": "button", "alias": "master_door_btns", "channel": 0} - ], - [ - {"type": "relay", "alias": "spotlight_main_front_1", "channel": 0}, - {"type": "button", "alias": "buttons_backdoor", "channel": 0}, - {"type": "button", "alias": "master_door_btns", "channel": 3} - ], - [ - {"type": "relay", "alias": "light_hub_1", "channel": 0}, - {"type": "button", "alias": "buttons_office_room", "channel": 3} - ], - [ - {"type": "relay", "alias": "craft_table_lamp", "channel": 0}, - {"type": "button", "alias": "buttons_office_room", "channel": 2} - ], - [ - {"type": "relay", "alias": "computer_table_lamp", "channel": 0}, - {"type": "button", "alias": "buttons_office_room", "channel": 1} - ], - [ - {"type": "relay", "alias": "light_hub_1", "channel": 2}, - {"type": "button", "alias": "buttons_office_room", "channel": 0}, - {"type": "button", "alias": "master_room_btns", "channel": 1}, - {"type": "button", "alias": "btns_hall2_1", "channel": 0}, - {"type": "button", "alias": "hall_secondary", "channel": 0}, - {"type": "button", "alias": "bed_btns_right_1", "channel": 0} - ], - [ - {"type": "relay", "alias": "light_hub_1", "channel": 1}, - {"type": "button", "alias": "master_room_btns", "channel": 0}, - {"type": "button", "alias": "bed_btns_right_1", "channel": 1}, - {"type": "button", "alias": "bed_btns_left", "channel": 1} - ], - [ - {"type": "relay", "alias": "bathroom_2_light", "channel": 0}, - {"type": "button", "alias": "hall_secondary", "channel": 1} - ], - [ - {"type": "relay", "alias": "plants_room_light", "channel": 0}, - {"type": "button", "alias": "plants_room_btns", "channel": 3} - ], - [ - {"type": "relay", "alias": "italy_lamp_relay", "channel": 0}, - {"type": "button", "alias": "plants_room_btns", "channel": 2} - ], - [ - {"type": "relay", "alias": "floor_lamp_relay", "channel": 0}, - {"type": "button", "alias": "plants_room_btns", "channel": 0} - ], - [ - {"type": "relay", "alias": "server_room_light", "channel": 0}, - {"type": "button", "alias": "btns_hall2_1", "channel": 1} - ], - [ - {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 0}, - {"type": "button", "alias": "kitchen_buttons_1", "channel": 0}, - {"type": "button", "alias": "kitchen_buttons_2", "channel": 0} - ], - [ - {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 1}, - {"type": "button", "alias": "kitchen_buttons_1", "channel": 1}, - {"type": "button", "alias": "kitchen_buttons_2", "channel": 1} - ], - [ - {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 2}, - {"type": "button", "alias": "first_hall_buttons1", "channel": 0} - ], - [ - {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 3}, - {"type": "button", "alias": "first_hall_buttons1", "channel": 1}, - {"type": "button", "alias": "first_hall_buttons2", "channel": 0} - ], - [ - {"type": "relay", "alias": "fisrt_floor_big_relay", "channel": 4}, - {"type": "button", "alias": "first_hall_buttons1", "channel": 2}, - {"type": "button", "alias": "first_hall_buttons2", "channel": 1} - ] -] \ No newline at end of file diff --git a/server/SHServ/App.php b/server/SHServ/App.php index a420ed8..a4d89a5 100644 --- a/server/SHServ/App.php +++ b/server/SHServ/App.php @@ -23,7 +23,6 @@ public $devtools; public $control_scripts_instances = []; - protected $required_control_scripts_instance; public function __construct() { parent::__construct(); @@ -145,16 +144,10 @@ public function control_scripts_init(): void { \SHServ\Middleware\ControlScripts::flush_statics(); - $this -> required_control_scripts_instance = new \SHServ\RequiredControlScriptsScope(); + $manifest_path = __DIR__ . "/../../automation/scopes-manifest.json"; + $manifest = json_decode(file_get_contents($manifest_path), true); - $scopes_dir = __DIR__ . "/../ControlScripts/Scopes/"; - $scripts_dir = scandir($scopes_dir); - $scripts = array_filter($scripts_dir, function($item) use ($scopes_dir) { - return !is_dir($scopes_dir . $item) and (pathinfo($item))["extension"] == "php"; - }); - - foreach($scripts as $script_name) { - $script_name = basename($script_name, ".php"); + foreach($manifest["scopes"] as $script_name) { $full_script_name = "\\ControlScripts\\Scopes\\{$script_name}"; $script = new $full_script_name(); diff --git a/server/SHServ/Controllers/CronController.php b/server/SHServ/Controllers/CronController.php index 015b004..2fb8bf7 100644 --- a/server/SHServ/Controllers/CronController.php +++ b/server/SHServ/Controllers/CronController.php @@ -22,6 +22,21 @@ } } + protected function run_script_cli(String $alias): int { + $output = []; + $returnCode = 0; + $consolePath = __DIR__ . "/../../console.php"; + exec("php " . escapeshellarg($consolePath) . " run-regular-script " . escapeshellarg($alias) . " 2>&1", $output, $returnCode); + if($returnCode !== 0) { + \Fury\Kernel\Logging::ins() -> set( + "CronController@run_regular_cron_scripts", + "Regular script {$alias} exited with code {$returnCode}", + implode("\n", $output) + ); + } + return $returnCode; + } + public function run_regular_cron_scripts() { $this -> ensure_localhost_only(); $scripts_model = new Scripts(); @@ -32,16 +47,7 @@ continue; } - try { - $script["script"](); - } catch (\Exception $e) { - \Fury\Kernel\Logging::ins() -> set( - "CronController@run_regular_cron_scripts", - "Regular script {$alias} failed", - $e -> getMessage() - ); - continue; - } + $this -> run_script_cli($alias); } } diff --git a/server/SHServ/Controllers/ScriptsRESTAPIController.php b/server/SHServ/Controllers/ScriptsRESTAPIController.php index 9007db8..c9c9dbb 100644 --- a/server/SHServ/Controllers/ScriptsRESTAPIController.php +++ b/server/SHServ/Controllers/ScriptsRESTAPIController.php @@ -94,33 +94,6 @@ return $this -> utils() -> response_success(["source" => $file]); } - public function scope_update($name, $path, $file) { - if(!strlen($file)) { - return $this -> utils() -> response_error("empty_field", ["file"]); - } - - $scripts_model = new Scripts(); - if(!$scripts_model -> scope_is_exists($name)) { - return $this -> utils() -> response_error("scope_not_found"); - } - - $allowed_dir = realpath(__DIR__ . "/../../ControlScripts/Scopes/"); - $filepath = realpath("{$path}/{$name}") ?: realpath("{$path}/{$name}.php"); - - if(!$filepath || strpos($filepath, $allowed_dir) !== 0) { - return $this -> utils() -> response_error("invalid_path", ["path"]); - } - - if(!file_exists($filepath)) { - return $this -> utils() -> response_error("file_not_exists"); - } - - $result = file_put_contents($filepath, $file); - return $result - ? $this -> utils() -> response_success(["result" => true]) - : $this -> utils() -> response_error("undefined_error"); - } - public function scope_remove($name) { return (new Scripts()) -> remove_scope($name) ? $this -> utils() -> response_success() diff --git a/server/SHServ/EventsHandlers.php b/server/SHServ/EventsHandlers.php index aa05749..e57332f 100644 --- a/server/SHServ/EventsHandlers.php +++ b/server/SHServ/EventsHandlers.php @@ -19,6 +19,14 @@ } }); + events() -> handler('app:online', function(Array $params) { + $device = $params["device"]; + $data = $params["data"]; + $device -> device_ip = $data["device_ip"]; + $device -> connection_status = "active"; + $device -> update(); + }); + if(FCONF["devmode"]) { events() -> handler("module:Template.start_making", function(Array $params) { app() -> devtools -> add_template_to_map( @@ -60,4 +68,4 @@ }); } } -} \ No newline at end of file +} diff --git a/server/SHServ/Middleware/ControlScripts.php b/server/SHServ/Middleware/ControlScripts.php index 7b660ca..8ad0699 100644 --- a/server/SHServ/Middleware/ControlScripts.php +++ b/server/SHServ/Middleware/ControlScripts.php @@ -136,7 +136,7 @@ $start_time = microtime(true); $result = self::$actions_scripts[$alias]["script"]($params); - + $exec_time = microtime(true) - $start_time; $exec_time = round($exec_time, 3); @@ -146,6 +146,30 @@ ]; } + public static function run_regular_script(String $alias): bool { + if(!isset(self::$regular_scripts[$alias])) { + return false; + } + + $scripts_model = new Scripts(); + if(!$scripts_model -> script_state("regular", $alias)) { + return false; + } + + try { + self::$regular_scripts[$alias]["script"](); + } catch(\Exception $e) { + \Fury\Kernel\Logging::ins() -> set( + "ControlScripts::run_regular_script", + "Regular script {$alias} failed", + $e -> getMessage() + ); + return false; + } + + return true; + } + protected function get_source_code($func): String { $ref_func = new \ReflectionFunction($func); $file_name = $ref_func -> getFileName(); diff --git a/server/SHServ/RequiredControlScriptsScope.php b/server/SHServ/RequiredControlScriptsScope.php deleted file mode 100644 index 2f124e8..0000000 --- a/server/SHServ/RequiredControlScriptsScope.php +++ /dev/null @@ -1,23 +0,0 @@ - add_event_handler("online", function(Device $device, Array $data) { - $device -> device_ip = $data["device_ip"]; - $device -> connection_status = "active"; - $device -> update(); - }); - } - - public function register_regular_scripts(): void { } - - public function register_actions_scripts(): void { } - -} \ No newline at end of file diff --git a/server/SHServ/Routes/ScriptsRESTAPI_v1.php b/server/SHServ/Routes/ScriptsRESTAPI_v1.php index 2866dc2..14e91b7 100644 --- a/server/SHServ/Routes/ScriptsRESTAPI_v1.php +++ b/server/SHServ/Routes/ScriptsRESTAPI_v1.php @@ -23,12 +23,6 @@ ); $this -> router -> post( - [ "name", "path", "file" ], - "{$this -> cn}\\ScriptsRESTAPIController@scope_update", - '/api/v1/scripts/scopes/update' - ); - - $this -> router -> post( [ "target_id", "place_in_area_id" ], "{$this -> cn}\\ScriptsRESTAPIController@place_in_area", "/api/v1/scripts/place-in-area" diff --git a/server/composer.json b/server/composer.json index 56a8e70..feea5cf 100644 --- a/server/composer.json +++ b/server/composer.json @@ -12,7 +12,7 @@ "classmap": [ "Fury/", "SHServ/", - "ControlScripts/" + "../automation/" ] }, "autoload-dev": { diff --git a/server/console.php b/server/console.php index c8a9653..b45f38f 100644 --- a/server/console.php +++ b/server/console.php @@ -1,5 +1,6 @@ tb = app() -> thin_builder; + $this -> tb -> query("CREATE TABLE scripts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + area_id INTEGER DEFAULT 0, + uniq_name TEXT, + type TEXT, + state TEXT, + create_at TEXT, + update_at TEXT + )"); + ControlScripts::flush_statics(); + } + + protected function tearDown(): void { + $this -> tb -> query("DROP TABLE IF EXISTS scripts"); + ControlScripts::flush_statics(); + } + + public function test_run_regular_script_returns_false_when_alias_missing(): void { + $this -> assertFalse(ControlScripts::run_regular_script('nonexistent')); + } + + public function test_run_regular_script_returns_false_when_disabled(): void { + $ref = new \ReflectionClass(ControlScripts::class); + $prop = $ref -> getProperty('regular_scripts'); + $prop -> setAccessible(true); + $prop -> setValue(null, [ + 'disabled' => [ + 'attributes' => ['alias' => 'disabled'], + 'code' => '', + 'script' => function() {} + ] + ]); + + $this -> tb -> insert('scripts', [ + 'uniq_name' => 'disabled', + 'type' => 'regular', + 'state' => 'disabled', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $this -> assertFalse(ControlScripts::run_regular_script('disabled')); + } + + public function test_run_regular_script_catches_exception(): void { + $ref = new \ReflectionClass(ControlScripts::class); + $prop = $ref -> getProperty('regular_scripts'); + $prop -> setAccessible(true); + $prop -> setValue(null, [ + 'fail' => [ + 'attributes' => ['alias' => 'fail'], + 'code' => '', + 'script' => function() { throw new \Exception('boom'); } + ] + ]); + + $this -> tb -> insert('scripts', [ + 'uniq_name' => 'fail', + 'type' => 'regular', + 'state' => 'enabled', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $this -> assertFalse(ControlScripts::run_regular_script('fail')); + } + + public function test_run_regular_script_returns_true_on_success(): void { + $ref = new \ReflectionClass(ControlScripts::class); + $prop = $ref -> getProperty('regular_scripts'); + $prop -> setAccessible(true); + $prop -> setValue(null, [ + 'ok' => [ + 'attributes' => ['alias' => 'ok'], + 'code' => '', + 'script' => function() { return true; } + ] + ]); + + $this -> tb -> insert('scripts', [ + 'uniq_name' => 'ok', + 'type' => 'regular', + 'state' => 'enabled', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $this -> assertTrue(ControlScripts::run_regular_script('ok')); + } +} diff --git a/server/tests/CronControllerTest.php b/server/tests/CronControllerTest.php index 73ed015..50ba1e3 100644 --- a/server/tests/CronControllerTest.php +++ b/server/tests/CronControllerTest.php @@ -4,6 +4,15 @@ use SHServ\Controllers\CronController; use SHServ\Middleware\ControlScripts; +class TestableCronController extends CronController { + public $cliCalls = []; + public $returnCodes = []; + protected function run_script_cli(String $alias): int { + $this -> cliCalls[] = $alias; + return $this -> returnCodes[$alias] ?? 0; + } +} + class CronControllerTest extends TestCase { protected $tb; @@ -26,10 +35,7 @@ ControlScripts::flush_statics(); } - public function test_regular_scripts_continue_after_failure(): void { - $executed = []; - - // Inject test regular scripts directly into ControlScripts static storage + public function test_regular_scripts_are_dispatched_to_cli(): void { $ref = new \ReflectionClass(ControlScripts::class); $prop = $ref -> getProperty('regular_scripts'); $prop -> setAccessible(true); @@ -38,22 +44,16 @@ 'fail_script' => [ 'attributes' => ['alias' => 'fail_script', 'name' => 'Fail'], 'code' => '', - 'script' => function() use (&$executed) { - $executed[] = 'fail'; - throw new \Exception('Intentional failure'); - } + 'script' => function() {} ], 'ok_script' => [ 'attributes' => ['alias' => 'ok_script', 'name' => 'OK'], 'code' => '', - 'script' => function() use (&$executed) { - $executed[] = 'ok'; - } + 'script' => function() {} ] ]; $prop -> setValue(null, $scripts); - // Enable both scripts in DB $this -> tb -> insert('scripts', [ 'uniq_name' => 'fail_script', 'type' => 'regular', @@ -68,9 +68,38 @@ ]); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - $controller = new CronController(); + $controller = new TestableCronController(); + $controller -> returnCodes = ['fail_script' => 1, 'ok_script' => 0]; $controller -> run_regular_cron_scripts(); - $this -> assertSame(['fail', 'ok'], $executed); + $this -> assertSame(['fail_script', 'ok_script'], $controller -> cliCalls); + } + + public function test_disabled_regular_scripts_are_skipped(): void { + $ref = new \ReflectionClass(ControlScripts::class); + $prop = $ref -> getProperty('regular_scripts'); + $prop -> setAccessible(true); + + $scripts = [ + 'disabled_script' => [ + 'attributes' => ['alias' => 'disabled_script', 'name' => 'Disabled'], + 'code' => '', + 'script' => function() {} + ] + ]; + $prop -> setValue(null, $scripts); + + $this -> tb -> insert('scripts', [ + 'uniq_name' => 'disabled_script', + 'type' => 'regular', + 'state' => 'disabled', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $controller = new TestableCronController(); + $controller -> run_regular_cron_scripts(); + + $this -> assertSame([], $controller -> cliCalls); } }