<?php
namespace SHServ\Helpers;
use \SHServ\Models\Devices;
class DeviceScriptsHelper {
protected $devices_model;
public function __construct(Devices $devices_model) {
$this -> devices_model = $devices_model;
}
public function devices(): Devices {
return $this -> devices_model;
}
/**
* Синхронизировать указанный канал реле с указанным каналом устройства кнопки.
* Синхронизация низкоуровневая и одностороняя, от реле к кнопке.
* @param Relay $relay
* @param Button $btn
* @param int|integer $relay_channel
* @param int|integer $btn_channel
* @return void
*/
public function sync_relay_to_btn_channel(\SHServ\Tools\DeviceAPI\Relay $relay_api, \SHServ\Tools\DeviceAPI\Button $btn_block_api, int $relay_channel = 0, int $btn_channel = 0): void {
$relay_channels = ($relay_api -> get_status())["channels"] ?? [];
if(!isset($relay_channels[$relay_channel]["state"])) {
return;
}
$btn_block_api -> set_channel_state(
$relay_channels[$relay_channel]["state"] == "on" ? "enabled" : "disabled",
$btn_channel
);
}
/**
* Синхронизировать состояние каналов реле с соответствующими блоками кнопок с указанными каналами.
* Синхронизация односторонняя от реле к кнопкам
* @param Array $sync_map [
* relay_channel_num => [
* ["alias" => "button_alias", "channel" => 1],
* ["alias" => "button_alias_2", "channel" => 0],
* ]
* ]
* @param String $relay_alias
* @return Bool
*/
public function sync_relay_to_btns(Array $sync_map, String $relay_alias): Bool {
if(isset($sync_map["connections"])) {
$sync_map = $this -> prepare_sync_map_by_alias($sync_map, $relay_alias);
}
$relay = $this -> devices() -> by_alias($relay_alias);
if(!$relay) {
return false;
}
$relay_api = $relay -> device_api();
if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) {
return false;
}
foreach($sync_map as $relay_channel => $btns) {
foreach($btns as $item) {
$btn = $this -> devices() -> by_alias($item["alias"]);
if(!$btn) {
continue;
}
$btn_api = $btn -> device_api();
if(!($btn_api instanceof \SHServ\Tools\DeviceAPI\Button)) {
continue;
}
$this -> sync_relay_to_btn_channel($relay_api, $btn_api, $relay_channel, intval($item["channel"]));
}
}
return true;
}
/**
* Синхронизировать состояние каналов устройства "кнопка" с соответствующими реле с указанными каналами.
* Синхронизация односторонняя от реле к кнопкам
* @param Array $sync_map [
* button_channel_num => [
* ["alias" => "relay_alias", "channel" => 1],
* ["alias" => "relay_alias_2", "channel" => 0],
* ]
* ]
* @param String $btn_alias
* @return Bool
*/
public function sync_btn_channels(Array $sync_map, String $btn_alias): Bool {
if(isset($sync_map["connections"])) {
$sync_map = $this -> prepare_sync_map_by_alias($sync_map, $btn_alias);
}
$btn = $this -> devices() -> by_alias($btn_alias);
if(!$btn) {
return false;
}
$btn_api = $btn -> device_api();
if(!($btn_api instanceof \SHServ\Tools\DeviceAPI\Button)) {
return false;
}
foreach($sync_map as $btn_channel => $relays) {
foreach($relays as $item) {
$relay = $this -> devices() -> by_alias($item["alias"]);
if(!$relay) {
continue;
}
$relay_api = $relay -> device_api();
if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) {
continue;
}
$this -> sync_relay_to_btn_channel($relay_api, $btn_api, intval($item["channel"]), $btn_channel);
}
}
return true;
}
public function get_sync_entries_by_type(Array $sync_map, String $type): Array {
$map = $sync_map["connections"];
$device_entries = [];
foreach($map as $connection) {
foreach($connection as $device_entry) {
if(!isset($device_entry["type"]) or $device_entry["type"] != $type) {
continue;
}
if(!isset($device_entry["alias"])) {
continue;
}
$device_entries[] = $device_entry;
}
}
return $device_entries;
}
public function prepare_sync_map_by_alias(Array $sync_map, String $alias): Array {
$result = [];
if(!isset($sync_map["connections"])) {
return $result;
}
foreach($sync_map["connections"] as $connection) {
foreach($connection as $device_entry) {
if($device_entry["alias"] != $alias) {
continue;
}
$result[$device_entry["channel"]] = array_filter($connection, function($i) use($alias) {
return $i["alias"] != $alias;
});
break;
}
}
return $result;
}
/**
* Групповая установка состояния (on/off) для списка реле.
* Targets: строка (alias одноканального реле) или [alias, channel] для многоканального.
* Автоматически синхронизирует индикаторы кнопок через sync_map.
* Возвращает массив результатов по каждому target.
*/
public function group_set_state(Array $targets, bool $state, Array $sync_map): Array {
$results = [];
foreach($targets as $target) {
$relay_alias = null;
$channel = null;
if(is_string($target)) {
$parts = explode(":", $target, 2);
$relay_alias = $parts[0];
if(isset($parts[1])) {
$channel = intval($parts[1]);
}
} elseif(is_array($target) && count($target) >= 2) {
$relay_alias = $target[0];
$channel = intval($target[1]);
} else {
continue;
}
$relay = $this -> devices() -> by_alias($relay_alias);
$key = $relay_alias . ($channel !== null ? ":{$channel}" : "");
if(!$relay) {
$results[$key] = false;
continue;
}
$relay_api = $relay -> device_api();
$result = false;
if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) {
if($channel === null) {
$result = $relay_api -> set_state($state);
} else {
$status = $relay_api -> get_status();
$current = $status["channels"][$channel]["state"] ?? null;
if($current != ($state ? "on" : "off")) {
$result = $relay_api -> set_channel_state($state, $channel);
} else {
$result = true;
}
}
$this -> sync_relay_to_btns($sync_map, $relay_alias);
}
$results[$key] = $result;
}
return $results;
}
/**
* Групповой toggle для списка реле.
* Targets: строка (alias) или [alias, channel].
* Автоматически синхронизирует индикаторы кнопок через sync_map.
*/
public function group_toggle(Array $targets, Array $sync_map): Array {
$results = [];
foreach($targets as $target) {
$relay_alias = null;
$channel = null;
if(is_string($target)) {
$parts = explode(":", $target, 2);
$relay_alias = $parts[0];
if(isset($parts[1])) {
$channel = intval($parts[1]);
}
} elseif(is_array($target) && count($target) >= 2) {
$relay_alias = $target[0];
$channel = intval($target[1]);
} else {
continue;
}
$relay = $this -> devices() -> by_alias($relay_alias);
$key = $relay_alias . ($channel !== null ? ":{$channel}" : "");
if(!$relay) {
$results[$key] = false;
continue;
}
$relay_api = $relay -> device_api();
$result = false;
if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) {
$result = $relay_api -> toggle_channel($channel ?? 0);
$this -> sync_relay_to_btns($sync_map, $relay_alias);
}
$results[$key] = $result;
}
return $results;
}
/**
* Получить состояние канала реле.
* @return bool|null — true = on, false = off, null = недоступно
*/
public function get_relay_state(string $relay_alias, int $channel = 0): ?bool {
try {
$relay = $this -> devices() -> by_alias($relay_alias);
if(!$relay) {
return null;
}
$relay_api = $relay -> device_api();
if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) {
return null;
}
$status = $relay_api -> get_status();
if(empty($status)) {
return null;
}
$channels = $status["channels"] ?? [];
if(!isset($channels[$channel]["state"])) {
return null;
}
return $channels[$channel]["state"] == "on";
} catch(\Throwable $e) {
return null;
}
}
/**
* Получить состояние люка.
* @return string|null — "open", "closed" или null
*/
public function get_hatch_state(string $hatch_alias): ?string {
try {
$hatch = $this -> devices() -> by_alias($hatch_alias);
if(!$hatch) {
return null;
}
$hatch_api = $hatch -> device_api();
if(!($hatch_api instanceof \SHServ\Tools\DeviceAPI\Hatch)) {
return null;
}
$status = $hatch_api -> get_status();
if(empty($status)) {
return null;
}
return $status["state"] ?? null;
} catch(\Throwable $e) {
return null;
}
}
/**
* Сформировать индикатор для одиночного реле.
* @return array — [["label" => "On|Off|?", "variant" => "success|secondary|secondary"]]
*/
public function make_relay_indicator(string $relay_alias, int $channel = 0): array {
$state = $this -> get_relay_state($relay_alias, $channel);
if($state === null) {
return [["label" => "?", "variant" => "secondary"]];
}
return [
["label" => $state ? "On" : "Off", "variant" => $state ? "success" : "secondary"]
];
}
/**
* Сформировать индикаторы для группы реле.
* @param array $targets — строки вида "alias:channel" или просто "alias"
* @return array — массив индикаторов
*/
public function make_group_indicators(array $targets): array {
$indicators = [];
foreach($targets as $target) {
$parts = explode(":", $target, 2);
$alias = $parts[0];
$channel = isset($parts[1]) ? intval($parts[1]) : 0;
$state = $this -> get_relay_state($alias, $channel);
if($state === null) {
$indicators[] = ["label" => "?", "variant" => "secondary"];
} else {
$indicators[] = ["label" => $state ? "On" : "Off", "variant" => $state ? "success" : "secondary"];
}
}
return $indicators;
}
/**
* Сформировать индикатор для люка.
* @return array — [["label" => "Open|Closed|?", "variant" => "warning|secondary|secondary"]]
*/
public function make_hatch_indicator(string $hatch_alias): array {
$state = $this -> get_hatch_state($hatch_alias);
if($state === null) {
return [["label" => "?", "variant" => "secondary"]];
}
$is_open = $state == "open";
return [
["label" => $is_open ? "Open" : "Closed", "variant" => $is_open ? "warning" : "secondary"]
];
}
}