mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 01:00:29 +08:00
gpio diff events mode
This commit is contained in:
parent
3852d0a456
commit
90d8e745e3
@ -42,10 +42,7 @@ class UserGpioApi:
|
|||||||
|
|
||||||
@exposed_http("GET", "/gpio")
|
@exposed_http("GET", "/gpio")
|
||||||
async def __state_handler(self, _: Request) -> Response:
|
async def __state_handler(self, _: Request) -> Response:
|
||||||
return make_json_response({
|
return make_json_response(await self.__user_gpio.get_state())
|
||||||
"model": (await self.__user_gpio.get_model()),
|
|
||||||
"state": (await self.__user_gpio.get_state()),
|
|
||||||
})
|
|
||||||
|
|
||||||
@exposed_http("POST", "/gpio/switch")
|
@exposed_http("POST", "/gpio/switch")
|
||||||
async def __switch_handler(self, req: Request) -> Response:
|
async def __switch_handler(self, req: Request) -> Response:
|
||||||
|
|||||||
@ -100,8 +100,9 @@ class StreamerH264NotSupported(OperationError):
|
|||||||
# =====
|
# =====
|
||||||
@dataclasses.dataclass(frozen=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class _SubsystemEventSource:
|
class _SubsystemEventSource:
|
||||||
get_state: (Callable[[], Coroutine[Any, Any, dict]] | None) = None
|
get_state: (Callable[[], Coroutine[Any, Any, dict]] | None) = None
|
||||||
poll_state: (Callable[[], AsyncGenerator[dict, None]] | None) = None
|
trigger_state: (Callable[[], Coroutine[Any, Any, None]] | None) = None
|
||||||
|
poll_state: (Callable[[], AsyncGenerator[dict, None]] | None) = None
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
@ -127,6 +128,7 @@ class _Subsystem:
|
|||||||
sub.add_source(
|
sub.add_source(
|
||||||
event_type=event_type,
|
event_type=event_type,
|
||||||
get_state=getattr(obj, "get_state", None),
|
get_state=getattr(obj, "get_state", None),
|
||||||
|
trigger_state=getattr(obj, "trigger_state", None),
|
||||||
poll_state=getattr(obj, "poll_state", None),
|
poll_state=getattr(obj, "poll_state", None),
|
||||||
)
|
)
|
||||||
return sub
|
return sub
|
||||||
@ -135,17 +137,20 @@ class _Subsystem:
|
|||||||
self,
|
self,
|
||||||
event_type: str,
|
event_type: str,
|
||||||
get_state: (Callable[[], Coroutine[Any, Any, dict]] | None),
|
get_state: (Callable[[], Coroutine[Any, Any, dict]] | None),
|
||||||
|
trigger_state: (Callable[[], Coroutine[Any, Any, None]] | None),
|
||||||
poll_state: (Callable[[], AsyncGenerator[dict, None]] | None),
|
poll_state: (Callable[[], AsyncGenerator[dict, None]] | None),
|
||||||
) -> "_Subsystem":
|
) -> "_Subsystem":
|
||||||
|
|
||||||
assert event_type
|
assert event_type
|
||||||
assert event_type not in self.sources, (self, event_type)
|
assert event_type not in self.sources, (self, event_type)
|
||||||
assert get_state or poll_state, (self, event_type)
|
assert get_state or poll_state, (self, event_type)
|
||||||
self.sources[event_type] = _SubsystemEventSource(get_state, poll_state)
|
self.sources[event_type] = _SubsystemEventSource(get_state, trigger_state, poll_state)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-instance-attributes
|
class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-instance-attributes
|
||||||
|
__EV_GPIO_STATE = "gpio_state"
|
||||||
|
|
||||||
def __init__( # pylint: disable=too-many-arguments,too-many-locals
|
def __init__( # pylint: disable=too-many-arguments,too-many-locals
|
||||||
self,
|
self,
|
||||||
auth_manager: AuthManager,
|
auth_manager: AuthManager,
|
||||||
@ -195,11 +200,11 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
|
|
||||||
self.__subsystems = [
|
self.__subsystems = [
|
||||||
_Subsystem.make(auth_manager, "Auth manager"),
|
_Subsystem.make(auth_manager, "Auth manager"),
|
||||||
_Subsystem.make(user_gpio, "User-GPIO", "gpio_state").add_source("gpio_model_state", user_gpio.get_model, None),
|
_Subsystem.make(user_gpio, "User-GPIO", self.__EV_GPIO_STATE),
|
||||||
_Subsystem.make(hid, "HID", "hid_state").add_source("hid_keymaps_state", self.__hid_api.get_keymaps, None),
|
_Subsystem.make(hid, "HID", "hid_state").add_source("hid_keymaps_state", self.__hid_api.get_keymaps, None, None),
|
||||||
_Subsystem.make(atx, "ATX", "atx_state"),
|
_Subsystem.make(atx, "ATX", "atx_state"),
|
||||||
_Subsystem.make(msd, "MSD", "msd_state"),
|
_Subsystem.make(msd, "MSD", "msd_state"),
|
||||||
_Subsystem.make(streamer, "Streamer", "streamer_state").add_source("streamer_ocr_state", self.__streamer_api.get_ocr, None),
|
_Subsystem.make(streamer, "Streamer", "streamer_state").add_source("streamer_ocr_state", self.__streamer_api.get_ocr, None, None),
|
||||||
*[
|
*[
|
||||||
_Subsystem.make(info_manager.get_submanager(sub), f"Info manager ({sub})", f"info_{sub}_state",)
|
_Subsystem.make(info_manager.get_submanager(sub), f"Info manager ({sub})", f"info_{sub}_state",)
|
||||||
for sub in sorted(info_manager.get_subs())
|
for sub in sorted(info_manager.get_subs())
|
||||||
@ -244,12 +249,13 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
@exposed_http("GET", "/ws")
|
@exposed_http("GET", "/ws")
|
||||||
async def __ws_handler(self, req: Request) -> WebSocketResponse:
|
async def __ws_handler(self, req: Request) -> WebSocketResponse:
|
||||||
stream = valid_bool(req.query.get("stream", True))
|
stream = valid_bool(req.query.get("stream", True))
|
||||||
async with self._ws_session(req, stream=stream) as ws:
|
legacy = valid_bool(req.query.get("legacy", True))
|
||||||
|
async with self._ws_session(req, stream=stream, legacy=legacy) as ws:
|
||||||
states = [
|
states = [
|
||||||
(event_type, src.get_state())
|
(event_type, src.get_state())
|
||||||
for sub in self.__subsystems
|
for sub in self.__subsystems
|
||||||
for (event_type, src) in sub.sources.items()
|
for (event_type, src) in sub.sources.items()
|
||||||
if src.get_state
|
if src.get_state and not src.trigger_state
|
||||||
]
|
]
|
||||||
events = dict(zip(
|
events = dict(zip(
|
||||||
map(operator.itemgetter(0), states),
|
map(operator.itemgetter(0), states),
|
||||||
@ -259,6 +265,10 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
ws.send_event(event_type, events.pop(event_type))
|
ws.send_event(event_type, events.pop(event_type))
|
||||||
for (event_type, _) in states
|
for (event_type, _) in states
|
||||||
])
|
])
|
||||||
|
for sub in self.__subsystems:
|
||||||
|
for src in sub.sources.values():
|
||||||
|
if src.trigger_state:
|
||||||
|
await src.trigger_state()
|
||||||
await ws.send_event("loop", {})
|
await ws.send_event("loop", {})
|
||||||
return (await self._ws_loop(ws))
|
return (await self._ws_loop(ws))
|
||||||
|
|
||||||
@ -347,12 +357,27 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
prev = cur
|
prev = cur
|
||||||
await self.__streamer_notifier.wait()
|
await self.__streamer_notifier.wait()
|
||||||
|
|
||||||
async def __poll_state(self, event_type: str, poller: AsyncGenerator[dict, None]) -> None:
|
|
||||||
async for state in poller:
|
|
||||||
await self._broadcast_ws_event(event_type, state)
|
|
||||||
|
|
||||||
async def __stream_snapshoter(self) -> None:
|
async def __stream_snapshoter(self) -> None:
|
||||||
await self.__snapshoter.run(
|
await self.__snapshoter.run(
|
||||||
is_live=self.__has_stream_clients,
|
is_live=self.__has_stream_clients,
|
||||||
notifier=self.__streamer_notifier,
|
notifier=self.__streamer_notifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def __poll_state(self, event_type: str, poller: AsyncGenerator[dict, None]) -> None:
|
||||||
|
if event_type == self.__EV_GPIO_STATE:
|
||||||
|
await self.__poll_gpio_state(poller)
|
||||||
|
else:
|
||||||
|
async for state in poller:
|
||||||
|
await self._broadcast_ws_event(event_type, state)
|
||||||
|
|
||||||
|
async def __poll_gpio_state(self, poller: AsyncGenerator[dict, None]) -> None:
|
||||||
|
prev: dict = {"state": {"inputs": {}, "outputs": {}}}
|
||||||
|
async for state in poller:
|
||||||
|
await self._broadcast_ws_event(self.__EV_GPIO_STATE, state, legacy=False)
|
||||||
|
if "model" in state: # We have only "model"+"state" or "model" event
|
||||||
|
prev = state
|
||||||
|
await self._broadcast_ws_event("gpio_model_state", prev["model"], legacy=True)
|
||||||
|
else:
|
||||||
|
prev["state"]["inputs"].update(state["state"].get("inputs", {}))
|
||||||
|
prev["state"]["outputs"].update(state["state"].get("outputs", {}))
|
||||||
|
await self._broadcast_ws_event(self.__EV_GPIO_STATE, prev["state"], legacy=True)
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import copy
|
||||||
|
|
||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
@ -68,12 +69,12 @@ class GpioChannelIsBusyError(IsBusyError, GpioError):
|
|||||||
class _GpioInput:
|
class _GpioInput:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
channel: str,
|
ch: str,
|
||||||
config: Section,
|
config: Section,
|
||||||
driver: BaseUserGpioDriver,
|
driver: BaseUserGpioDriver,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
self.__channel = channel
|
self.__ch = ch
|
||||||
self.__pin: str = str(config.pin)
|
self.__pin: str = str(config.pin)
|
||||||
self.__inverted: bool = config.inverted
|
self.__inverted: bool = config.inverted
|
||||||
|
|
||||||
@ -100,7 +101,7 @@ class _GpioInput:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"Input({self.__channel}, driver={self.__driver}, pin={self.__pin})"
|
return f"Input({self.__ch}, driver={self.__driver}, pin={self.__pin})"
|
||||||
|
|
||||||
__repr__ = __str__
|
__repr__ = __str__
|
||||||
|
|
||||||
@ -108,13 +109,13 @@ class _GpioInput:
|
|||||||
class _GpioOutput: # pylint: disable=too-many-instance-attributes
|
class _GpioOutput: # pylint: disable=too-many-instance-attributes
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
channel: str,
|
ch: str,
|
||||||
config: Section,
|
config: Section,
|
||||||
driver: BaseUserGpioDriver,
|
driver: BaseUserGpioDriver,
|
||||||
notifier: aiotools.AioNotifier,
|
notifier: aiotools.AioNotifier,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
self.__channel = channel
|
self.__ch = ch
|
||||||
self.__pin: str = str(config.pin)
|
self.__pin: str = str(config.pin)
|
||||||
self.__inverted: bool = config.inverted
|
self.__inverted: bool = config.inverted
|
||||||
|
|
||||||
@ -224,7 +225,7 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
|
|||||||
await self.__driver.write(self.__pin, (state ^ self.__inverted))
|
await self.__driver.write(self.__pin, (state ^ self.__inverted))
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"Output({self.__channel}, driver={self.__driver}, pin={self.__pin})"
|
return f"Output({self.__ch}, driver={self.__driver}, pin={self.__pin})"
|
||||||
|
|
||||||
__repr__ = __str__
|
__repr__ = __str__
|
||||||
|
|
||||||
@ -232,9 +233,8 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
|
|||||||
# =====
|
# =====
|
||||||
class UserGpio:
|
class UserGpio:
|
||||||
def __init__(self, config: Section, otg_config: Section) -> None:
|
def __init__(self, config: Section, otg_config: Section) -> None:
|
||||||
self.__view = config.view
|
|
||||||
|
|
||||||
self.__notifier = aiotools.AioNotifier()
|
self.__notifier = aiotools.AioNotifier()
|
||||||
|
self.__full_state_requested = True
|
||||||
|
|
||||||
self.__drivers = {
|
self.__drivers = {
|
||||||
driver: get_ugpio_driver_class(drv_config.type)(
|
driver: get_ugpio_driver_class(drv_config.type)(
|
||||||
@ -249,45 +249,64 @@ class UserGpio:
|
|||||||
self.__inputs: dict[str, _GpioInput] = {}
|
self.__inputs: dict[str, _GpioInput] = {}
|
||||||
self.__outputs: dict[str, _GpioOutput] = {}
|
self.__outputs: dict[str, _GpioOutput] = {}
|
||||||
|
|
||||||
for (channel, ch_config) in tools.sorted_kvs(config.scheme):
|
for (ch, ch_config) in tools.sorted_kvs(config.scheme):
|
||||||
driver = self.__drivers[ch_config.driver]
|
driver = self.__drivers[ch_config.driver]
|
||||||
if ch_config.mode == UserGpioModes.INPUT:
|
if ch_config.mode == UserGpioModes.INPUT:
|
||||||
self.__inputs[channel] = _GpioInput(channel, ch_config, driver)
|
self.__inputs[ch] = _GpioInput(ch, ch_config, driver)
|
||||||
else: # output:
|
else: # output:
|
||||||
self.__outputs[channel] = _GpioOutput(channel, ch_config, driver, self.__notifier)
|
self.__outputs[ch] = _GpioOutput(ch, ch_config, driver, self.__notifier)
|
||||||
|
|
||||||
async def get_model(self) -> dict:
|
self.__scheme = self.__make_scheme()
|
||||||
return {
|
self.__view = self.__make_view(config.view)
|
||||||
"scheme": {
|
|
||||||
"inputs": {channel: gin.get_scheme() for (channel, gin) in self.__inputs.items()},
|
|
||||||
"outputs": {
|
|
||||||
channel: gout.get_scheme()
|
|
||||||
for (channel, gout) in self.__outputs.items()
|
|
||||||
if not gout.is_const()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"view": self.__make_view(),
|
|
||||||
}
|
|
||||||
|
|
||||||
async def get_state(self) -> dict:
|
async def get_state(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"inputs": {channel: await gin.get_state() for (channel, gin) in self.__inputs.items()},
|
"model": {
|
||||||
|
"scheme": copy.deepcopy(self.__scheme),
|
||||||
|
"view": copy.deepcopy(self.__view),
|
||||||
|
},
|
||||||
|
"state": (await self.__get_io_state()),
|
||||||
|
}
|
||||||
|
|
||||||
|
async def trigger_state(self) -> None:
|
||||||
|
self.__full_state_requested = True
|
||||||
|
self.__notifier.notify()
|
||||||
|
|
||||||
|
async def poll_state(self) -> AsyncGenerator[dict, None]:
|
||||||
|
prev: dict = {"inputs": {}, "outputs": {}}
|
||||||
|
while True: # pylint: disable=too-many-nested-blocks
|
||||||
|
if self.__full_state_requested:
|
||||||
|
self.__full_state_requested = False
|
||||||
|
full = await self.get_state()
|
||||||
|
prev = copy.deepcopy(full["state"])
|
||||||
|
yield full
|
||||||
|
else:
|
||||||
|
new = await self.__get_io_state()
|
||||||
|
diff: dict = {}
|
||||||
|
for sub in ["inputs", "outputs"]:
|
||||||
|
for ch in new[sub]:
|
||||||
|
if new[sub][ch] != prev[sub][ch]:
|
||||||
|
if sub not in diff:
|
||||||
|
diff[sub] = {}
|
||||||
|
diff[sub][ch] = new[sub][ch]
|
||||||
|
if diff:
|
||||||
|
prev = copy.deepcopy(new)
|
||||||
|
yield {"state": diff}
|
||||||
|
await self.__notifier.wait()
|
||||||
|
|
||||||
|
async def __get_io_state(self) -> dict:
|
||||||
|
return {
|
||||||
|
"inputs": {
|
||||||
|
ch: (await gin.get_state())
|
||||||
|
for (ch, gin) in self.__inputs.items()
|
||||||
|
},
|
||||||
"outputs": {
|
"outputs": {
|
||||||
channel: await gout.get_state()
|
ch: (await gout.get_state())
|
||||||
for (channel, gout) in self.__outputs.items()
|
for (ch, gout) in self.__outputs.items()
|
||||||
if not gout.is_const()
|
if not gout.is_const()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async def poll_state(self) -> AsyncGenerator[dict, None]:
|
|
||||||
prev_state: dict = {}
|
|
||||||
while True:
|
|
||||||
state = await self.get_state()
|
|
||||||
if state != prev_state:
|
|
||||||
yield state
|
|
||||||
prev_state = state
|
|
||||||
await self.__notifier.wait()
|
|
||||||
|
|
||||||
def sysprep(self) -> None:
|
def sysprep(self) -> None:
|
||||||
get_logger(0).info("Preparing User-GPIO drivers ...")
|
get_logger(0).info("Preparing User-GPIO drivers ...")
|
||||||
for (_, driver) in tools.sorted_kvs(self.__drivers):
|
for (_, driver) in tools.sorted_kvs(self.__drivers):
|
||||||
@ -307,28 +326,43 @@ class UserGpio:
|
|||||||
except Exception:
|
except Exception:
|
||||||
get_logger().exception("Can't cleanup driver %s", driver)
|
get_logger().exception("Can't cleanup driver %s", driver)
|
||||||
|
|
||||||
async def switch(self, channel: str, state: bool, wait: bool) -> None:
|
async def switch(self, ch: str, state: bool, wait: bool) -> None:
|
||||||
gout = self.__outputs.get(channel)
|
gout = self.__outputs.get(ch)
|
||||||
if gout is None:
|
if gout is None:
|
||||||
raise GpioChannelNotFoundError()
|
raise GpioChannelNotFoundError()
|
||||||
await gout.switch(state, wait)
|
await gout.switch(state, wait)
|
||||||
|
|
||||||
async def pulse(self, channel: str, delay: float, wait: bool) -> None:
|
async def pulse(self, ch: str, delay: float, wait: bool) -> None:
|
||||||
gout = self.__outputs.get(channel)
|
gout = self.__outputs.get(ch)
|
||||||
if gout is None:
|
if gout is None:
|
||||||
raise GpioChannelNotFoundError()
|
raise GpioChannelNotFoundError()
|
||||||
await gout.pulse(delay, wait)
|
await gout.pulse(delay, wait)
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
|
||||||
def __make_view(self) -> dict:
|
def __make_scheme(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"header": {"title": self.__make_view_title()},
|
"inputs": {
|
||||||
"table": self.__make_view_table(),
|
ch: gin.get_scheme()
|
||||||
|
for (ch, gin) in self.__inputs.items()
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
ch: gout.get_scheme()
|
||||||
|
for (ch, gout) in self.__outputs.items()
|
||||||
|
if not gout.is_const()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __make_view_title(self) -> list[dict]:
|
# =====
|
||||||
raw_title = self.__view["header"]["title"]
|
|
||||||
|
def __make_view(self, view: dict) -> dict:
|
||||||
|
return {
|
||||||
|
"header": {"title": self.__make_view_title(view)},
|
||||||
|
"table": self.__make_view_table(view),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __make_view_title(self, view: dict) -> list[dict]:
|
||||||
|
raw_title = view["header"]["title"]
|
||||||
title: list[dict] = []
|
title: list[dict] = []
|
||||||
if isinstance(raw_title, list):
|
if isinstance(raw_title, list):
|
||||||
for item in raw_title:
|
for item in raw_title:
|
||||||
@ -342,9 +376,9 @@ class UserGpio:
|
|||||||
title.append(self.__make_item_label(f"#{raw_title}"))
|
title.append(self.__make_item_label(f"#{raw_title}"))
|
||||||
return title
|
return title
|
||||||
|
|
||||||
def __make_view_table(self) -> list[list[dict] | None]:
|
def __make_view_table(self, view: dict) -> list[list[dict] | None]:
|
||||||
table: list[list[dict] | None] = []
|
table: list[list[dict] | None] = []
|
||||||
for row in self.__view["table"]:
|
for row in view["table"]:
|
||||||
if len(row) == 0:
|
if len(row) == 0:
|
||||||
table.append(None)
|
table.append(None)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -384,7 +384,7 @@ class HttpServer:
|
|||||||
break
|
break
|
||||||
return ws.wsr
|
return ws.wsr
|
||||||
|
|
||||||
async def _broadcast_ws_event(self, event_type: str, event: (dict | None)) -> None:
|
async def _broadcast_ws_event(self, event_type: str, event: (dict | None), legacy: (bool | None)=None) -> None:
|
||||||
if self.__ws_sessions:
|
if self.__ws_sessions:
|
||||||
await asyncio.gather(*[
|
await asyncio.gather(*[
|
||||||
ws.send_event(event_type, event)
|
ws.send_event(event_type, event)
|
||||||
@ -393,6 +393,7 @@ class HttpServer:
|
|||||||
not ws.wsr.closed
|
not ws.wsr.closed
|
||||||
and ws.wsr._req is not None # pylint: disable=protected-access
|
and ws.wsr._req is not None # pylint: disable=protected-access
|
||||||
and ws.wsr._req.transport is not None # pylint: disable=protected-access
|
and ws.wsr._req.transport is not None # pylint: disable=protected-access
|
||||||
|
and (legacy is None or ws.kwargs.get("legacy") == legacy)
|
||||||
)
|
)
|
||||||
], return_exceptions=True)
|
], return_exceptions=True)
|
||||||
|
|
||||||
|
|||||||
@ -32,26 +32,18 @@ export function Gpio(__recorder) {
|
|||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
var __state = null;
|
var __has_model = false;
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
self.setState = function(state) {
|
self.setState = function(state) {
|
||||||
if (state) {
|
if (state) {
|
||||||
for (let ch in state.inputs) {
|
if (state.model) {
|
||||||
for (let el of $$(`__gpio-led-${ch}`)) {
|
__applyModel(state.model);
|
||||||
__setLedState(el, state.inputs[ch].state);
|
__has_model = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (let ch in state.outputs) {
|
if (__has_model && state.state) {
|
||||||
for (let type of ["switch", "button"]) {
|
__applyState(state.state);
|
||||||
for (let el of $$(`__gpio-${type}-${ch}`)) {
|
|
||||||
tools.el.setEnabled(el, state.outputs[ch].online && !state.outputs[ch].busy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let el of $$(`__gpio-switch-${ch}`)) {
|
|
||||||
el.checked = state.outputs[ch].state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let el of $$("__gpio-led")) {
|
for (let el of $$("__gpio-led")) {
|
||||||
@ -62,11 +54,33 @@ export function Gpio(__recorder) {
|
|||||||
tools.el.setEnabled(el, false);
|
tools.el.setEnabled(el, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
__has_model = false;
|
||||||
}
|
}
|
||||||
__state = state;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.setModel = function(model) {
|
var __applyState = function(state) {
|
||||||
|
if (state.inputs) {
|
||||||
|
for (let ch in state.inputs) {
|
||||||
|
for (let el of $$(`__gpio-led-${ch}`)) {
|
||||||
|
__setLedState(el, state.inputs[ch].state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state.outputs) {
|
||||||
|
for (let ch in state.outputs) {
|
||||||
|
for (let type of ["switch", "button"]) {
|
||||||
|
for (let el of $$(`__gpio-${type}-${ch}`)) {
|
||||||
|
tools.el.setEnabled(el, state.outputs[ch].online && !state.outputs[ch].busy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let el of $$(`__gpio-switch-${ch}`)) {
|
||||||
|
el.checked = state.outputs[ch].state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var __applyModel = function(model) {
|
||||||
tools.feature.setEnabled($("gpio-dropdown"), model.view.table.length);
|
tools.feature.setEnabled($("gpio-dropdown"), model.view.table.length);
|
||||||
if (model.view.table.length) {
|
if (model.view.table.length) {
|
||||||
let title = [];
|
let title = [];
|
||||||
@ -81,23 +95,23 @@ export function Gpio(__recorder) {
|
|||||||
$("gpio-menu-button").innerHTML = title.join(" ");
|
$("gpio-menu-button").innerHTML = title.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = "<table class=\"kv\">";
|
let html = "<table class=\"kv\">";
|
||||||
for (let row of model.view.table) {
|
for (let row of model.view.table) {
|
||||||
if (row === null) {
|
if (row === null) {
|
||||||
content += "</table><hr><table class=\"kv\">";
|
html += "</table><hr><table class=\"kv\">";
|
||||||
} else {
|
} else {
|
||||||
content += "<tr>";
|
html += "<tr>";
|
||||||
for (let item of row) {
|
for (let item of row) {
|
||||||
if (item.type === "output") {
|
if (item.type === "output") {
|
||||||
item.scheme = model.scheme.outputs[item.channel];
|
item.scheme = model.scheme.outputs[item.channel];
|
||||||
}
|
}
|
||||||
content += `<td align="center">${__createItem(item)}</td>`;
|
html += `<td align="center">${__createItem(item)}</td>`;
|
||||||
}
|
}
|
||||||
content += "</tr>";
|
html += "</tr>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content += "</table>";
|
html += "</table>";
|
||||||
$("gpio-menu").innerHTML = content;
|
$("gpio-menu").innerHTML = html;
|
||||||
|
|
||||||
for (let ch in model.scheme.outputs) {
|
for (let ch in model.scheme.outputs) {
|
||||||
for (let el of $$(`__gpio-switch-${ch}`)) {
|
for (let el of $$(`__gpio-switch-${ch}`)) {
|
||||||
@ -111,8 +125,6 @@ export function Gpio(__recorder) {
|
|||||||
tools.feature.setEnabled($("v3-usb-breaker"), ("__v3_usb_breaker__" in model.scheme.outputs));
|
tools.feature.setEnabled($("v3-usb-breaker"), ("__v3_usb_breaker__" in model.scheme.outputs));
|
||||||
tools.feature.setEnabled($("v4-locator"), ("__v4_locator__" in model.scheme.outputs));
|
tools.feature.setEnabled($("v4-locator"), ("__v4_locator__" in model.scheme.outputs));
|
||||||
tools.feature.setEnabled($("system-tool-wol"), ("__wol__" in model.scheme.outputs));
|
tools.feature.setEnabled($("system-tool-wol"), ("__wol__" in model.scheme.outputs));
|
||||||
|
|
||||||
self.setState(__state);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var __createItem = function(item) {
|
var __createItem = function(item) {
|
||||||
@ -167,9 +179,9 @@ export function Gpio(__recorder) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var __setLedState = function(el, state) {
|
var __setLedState = function(el, on) {
|
||||||
let color = el.getAttribute("data-color");
|
let color = el.getAttribute("data-color");
|
||||||
if (state) {
|
if (on) {
|
||||||
el.classList.add(`led-${color}`);
|
el.classList.add(`led-${color}`);
|
||||||
el.classList.remove("led-gray");
|
el.classList.remove("led-gray");
|
||||||
} else {
|
} else {
|
||||||
@ -221,7 +233,7 @@ export function Gpio(__recorder) {
|
|||||||
var __sendPost = function(url, params) {
|
var __sendPost = function(url, params) {
|
||||||
tools.httpPost(url, params, function(http) {
|
tools.httpPost(url, params, function(http) {
|
||||||
if (http.status === 409) {
|
if (http.status === 409) {
|
||||||
wm.error("Performing another operation for this GPIO channel.<br>Please try again later");
|
wm.error("Performing another operation for this GPIO channel.<br>Please try again later.");
|
||||||
} else if (http.status !== 200) {
|
} else if (http.status !== 200) {
|
||||||
wm.error("GPIO error", http.responseText);
|
wm.error("GPIO error", http.responseText);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -282,7 +282,7 @@ export function Session() {
|
|||||||
|
|
||||||
tools.httpGet("/api/auth/check", null, function(http) {
|
tools.httpGet("/api/auth/check", null, function(http) {
|
||||||
if (http.status === 200) {
|
if (http.status === 200) {
|
||||||
__ws = new WebSocket(`${tools.is_https ? "wss" : "ws"}://${location.host}/api/ws`);
|
__ws = new WebSocket(`${tools.is_https ? "wss" : "ws"}://${location.host}/api/ws?legacy=0`);
|
||||||
__ws.sendHidEvent = (event) => __sendHidEvent(__ws, event.event_type, event.event);
|
__ws.sendHidEvent = (event) => __sendHidEvent(__ws, event.event_type, event.event);
|
||||||
__ws.onopen = __wsOpenHandler;
|
__ws.onopen = __wsOpenHandler;
|
||||||
__ws.onmessage = __wsMessageHandler;
|
__ws.onmessage = __wsMessageHandler;
|
||||||
@ -359,7 +359,6 @@ export function Session() {
|
|||||||
case "info_fan_state": __setAboutInfoFan(data.event); break;
|
case "info_fan_state": __setAboutInfoFan(data.event); break;
|
||||||
case "info_system_state": __setAboutInfoSystem(data.event); break;
|
case "info_system_state": __setAboutInfoSystem(data.event); break;
|
||||||
case "info_extras_state": __setExtras(data.event); break;
|
case "info_extras_state": __setExtras(data.event); break;
|
||||||
case "gpio_model_state": __gpio.setModel(data.event); break;
|
|
||||||
case "gpio_state": __gpio.setState(data.event); break;
|
case "gpio_state": __gpio.setState(data.event); break;
|
||||||
case "hid_keymaps_state": __hid.setKeymaps(data.event); break;
|
case "hid_keymaps_state": __hid.setKeymaps(data.event); break;
|
||||||
case "hid_state": __hid.setState(data.event); break;
|
case "hid_state": __hid.setState(data.event); break;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user