Skip to content

Commit 26d41f1

Browse files
committed
Merged branch sdk_plugin_testing
1 parent 9d95322 commit 26d41f1

File tree

6 files changed

+221
-6
lines changed

6 files changed

+221
-6
lines changed

openrgb/network.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,32 @@ def read(self):
167167
finally:
168168
self.lock.release()
169169
self.callback(device_id, packet_type, utils.parse_list(utils.Profile, idata, self._protocol_version))
170+
elif packet_type == utils.PacketType.REQUEST_PLUGIN_LIST:
171+
try:
172+
data = bytes()
173+
while len(data) < packet_size:
174+
data += self.sock.recv(packet_size - len(data))
175+
idata = iter(data)
176+
for _ in range(4):
177+
next(idata)
178+
except utils.CONNECTION_ERRORS as e:
179+
self.stop_connection()
180+
raise utils.OpenRGBDisconnected() from e
181+
finally:
182+
self.lock.release()
183+
self.callback(device_id, packet_type, utils.parse_list(utils.Plugin, idata, self._protocol_version))
184+
elif packet_type == utils.PacketType.PLUGIN_SPECIFIC:
185+
try:
186+
data = bytes()
187+
while len(data) < packet_size:
188+
data += self.sock.recv(packet_size - len(data))
189+
idata = iter(data)
190+
except utils.CONNECTION_ERRORS as e:
191+
self.stop_connection()
192+
raise utils.OpenRGBDisconnected() from e
193+
finally:
194+
self.lock.release()
195+
self.callback(device_id, packet_type, idata)
170196

171197
def requestDeviceData(self, device: int):
172198
'''
@@ -194,7 +220,14 @@ def requestProfileList(self):
194220
self.send_header(0, utils.PacketType.REQUEST_PROFILE_LIST, 0)
195221
self.read()
196222

197-
def send_header(self, device_id: int, packet_type: utils.PacketType, packet_size: int):
223+
def requestPluginList(self):
224+
'''
225+
Sends the request for the available plugins
226+
'''
227+
self.send_header(0, utils.PacketType.REQUEST_PLUGIN_LIST, 0)
228+
self.read()
229+
230+
def send_header(self, device_id: int, packet_type: utils.PacketType, packet_size: int, release_lock: bool = True):
198231
'''
199232
Sends a header to the SDK
200233
@@ -215,10 +248,12 @@ def send_header(self, device_id: int, packet_type: utils.PacketType, packet_size
215248
if sent != len(data):
216249
self.stop_connection()
217250
raise utils.OpenRGBDisconnected()
218-
if packet_size == 0 and packet_type not in (utils.PacketType.REQUEST_CONTROLLER_COUNT,
219-
utils.PacketType.REQUEST_CONTROLLER_DATA,
220-
utils.PacketType.REQUEST_PROTOCOL_VERSION,
221-
utils.PacketType.REQUEST_PROFILE_LIST):
251+
if release_lock and packet_size == 0 and packet_type not in (utils.PacketType.REQUEST_CONTROLLER_COUNT,
252+
utils.PacketType.REQUEST_CONTROLLER_DATA,
253+
utils.PacketType.REQUEST_PROTOCOL_VERSION,
254+
utils.PacketType.REQUEST_PROFILE_LIST,
255+
utils.PacketType.REQUEST_PLUGIN_LIST,
256+
utils.PacketType.PLUGIN_SPECIFIC):
222257
self.lock.release()
223258
except utils.CONNECTION_ERRORS as e:
224259
self.stop_connection()
@@ -257,6 +292,9 @@ def check_version(self, packet_type: utils.PacketType):
257292
raise utils.SDKVersionError("Profile controls not supported on protocol versions < 2. You probably need to update OpenRGB")
258293
elif self._protocol_version < 3 and packet_type == utils.PacketType.RGBCONTROLLER_SAVEMODE:
259294
raise utils.SDKVersionError("Saving modes not supported on protocol versions < 3. You probably need to update OpenRGB")
295+
elif self._protocol_version < 4 and packet_type in (utils.PacketType.REQUEST_PLUGIN_LIST,
296+
utils.PacketType.PLUGIN_SPECIFIC):
297+
raise utils.SDKVersionError("Plugin controls not supported on protocol versions < 4. You probably need to update OpenRGB")
260298

261299
@property
262300
def connected(self) -> bool:

openrgb/orgb.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from openrgb import utils
55
from typing import Union, Any, Optional
66
from openrgb.network import NetworkClient
7-
# from dataclasses import dataclass
7+
from openrgb.plugins import create_plugin, ORGBPlugin
8+
from time import sleep
89
from os import environ
910

1011

@@ -376,6 +377,7 @@ def __init__(self, address: str = "127.0.0.1", port: int = 6742, name: str = "op
376377
self.device_num = 0
377378
self.devices: list[Device] = []
378379
self.profiles: list[utils.Profile] = []
380+
self.plugins: list[ORGBPlugin] = []
379381
self.comms = NetworkClient(
380382
self._callback, address, port, name, protocol_version)
381383
self.address = address
@@ -407,6 +409,12 @@ def _callback(self, device: int, type: int, data: Any):
407409
self.comms.requestDeviceNum()
408410
elif type == utils.PacketType.REQUEST_PROFILE_LIST:
409411
self.profiles = data
412+
elif type == utils.PacketType.REQUEST_PLUGIN_LIST:
413+
for plugin in data:
414+
if (all(plugin.id != existing.id for existing in self.plugins)):
415+
self.plugins.append(create_plugin(plugin, self.comms))
416+
elif type == utils.PacketType.PLUGIN_SPECIFIC:
417+
next(plugin for plugin in self.plugins if plugin.id == device)._recv(data)
410418

411419
def set_color(self, color: utils.RGBColor, fast: bool = False):
412420
'''
@@ -562,13 +570,23 @@ def update(self):
562570
self.comms.requestDeviceData(x)
563571
if self.comms._protocol_version >= 2:
564572
self.update_profiles()
573+
if self.comms._protocol_version >= 4:
574+
self.update_plugins()
565575

566576
def update_profiles(self):
567577
'''
568578
Gets the list of available profiles from the server.
569579
'''
570580
self.comms.requestProfileList()
571581

582+
def update_plugins(self):
583+
'''
584+
Gets the list of enabled plugins from the server.
585+
'''
586+
self.comms.requestPluginList()
587+
for plugin in self.plugins:
588+
plugin.update()
589+
572590
def show(self, fast: bool = False, force: bool = False):
573591
'''
574592
Shows all devices

openrgb/plugins/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from __future__ import annotations
2+
from openrgb.utils import Plugin
3+
from openrgb.network import NetworkClient
4+
from openrgb.plugins.common import ORGBPlugin
5+
6+
from openrgb.plugins.effects import EffectsPlugin
7+
8+
PLUGIN_NAMES = {
9+
"OpenRGB Effects Plugin": EffectsPlugin
10+
}
11+
12+
13+
def create_plugin(plugin: Plugin, comms: NetworkClient) -> ORGBPlugin:
14+
return PLUGIN_NAMES[plugin.name](plugin, comms)

openrgb/plugins/common.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from __future__ import annotations
2+
from openrgb import utils
3+
from openrgb.network import NetworkClient
4+
from typing import Iterator, Optional, Type
5+
from enum import IntEnum
6+
import struct
7+
8+
9+
class ORGBPlugin:
10+
version = -1
11+
pkt_type_enum = object()
12+
13+
def __init__(self, plugin_data: utils.Plugin, comms: NetworkClient):
14+
self.name = plugin_data.name
15+
self.description = plugin_data.description
16+
self.plugin_version = plugin_data.version
17+
self.id = plugin_data.id
18+
self.sdk_version = plugin_data.sdk_version
19+
self.comms = comms
20+
assert self.sdk_version == self.version
21+
22+
def __repr__(self):
23+
return type(self).__name__
24+
25+
def send_packet(self, packet_type: int, data: Optional[bytes] = None, request: bool = False):
26+
if not data:
27+
data = bytes()
28+
self.comms.send_header(self.id, utils.PacketType.PLUGIN_SPECIFIC, len(data) + struct.calcsize('I'), not request)
29+
data = struct.pack('I', packet_type) + data
30+
self.comms.send_data(data, not request)
31+
32+
def _recv(self, data: Iterator[int]):
33+
pkt_id = self.pkt_type_enum(utils.parse_var('I', data)) # type: ignore
34+
self.recv(pkt_id, data)
35+
36+
def recv(self, pkt_id: Type[IntEnum], data: Iterator[int]):
37+
# To be implemented per plugin
38+
pass
39+
40+
def update(self):
41+
# Optional, called with `OpenRGBClient.update_plugins`
42+
pass

openrgb/plugins/effects.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from __future__ import annotations
2+
from openrgb import utils
3+
from openrgb.plugins import ORGBPlugin
4+
from dataclasses import dataclass
5+
from typing import Iterator, Union
6+
from enum import IntEnum
7+
8+
class EffectPacketType(IntEnum):
9+
REQUEST_EFFECT_LIST = 0
10+
START_EFFECT = 20
11+
STOP_EFFECT = 21
12+
13+
14+
@dataclass
15+
class Effect:
16+
name: str
17+
description: str
18+
enabled: bool
19+
20+
@classmethod
21+
def unpack(cls, data: Iterator[int], version: int, *args) -> Effect:
22+
name = utils.parse_string(data)
23+
description = utils.parse_string(data)
24+
enabled = utils.parse_var('?', data)
25+
26+
return cls(
27+
name,
28+
description,
29+
enabled
30+
)
31+
32+
33+
class EffectsPlugin(ORGBPlugin):
34+
version = 1
35+
pkt_type_enum = EffectPacketType
36+
37+
def __init__(self, *args, **kwargs):
38+
super().__init__(*args, **kwargs)
39+
self.effects = []
40+
41+
def update(self):
42+
self.send_packet(EffectPacketType.REQUEST_EFFECT_LIST, request=True)
43+
self.comms.read()
44+
45+
def recv(self, pkt_id: EffectPacketType, data: Iterator[int]): # type: ignore
46+
if pkt_id == EffectPacketType.REQUEST_EFFECT_LIST:
47+
self.effects = utils.parse_list(Effect, data, self.version)
48+
49+
def start_effect(self, effect: Union[int, str, Effect]):
50+
if type(effect) == int:
51+
effect = self.effects[effect] # type: ignore
52+
elif type(effect) == str:
53+
try:
54+
effect = next(m for m in self.effects if m.name.lower() == effect.lower()) # type: ignore
55+
except StopIteration as e:
56+
raise ValueError(f"Effect `{effect}` not found") from e
57+
data = utils.pack_string(effect.name) # type: ignore
58+
59+
self.send_packet(EffectPacketType.START_EFFECT, data)
60+
61+
def stop_effect(self, effect: Union[int, str, Effect]):
62+
if type(effect) == int:
63+
effect = self.effects[effect] # type: ignore
64+
elif type(effect) == str:
65+
try:
66+
effect = next(m for m in self.effects if m.name.lower() == effect.lower()) # type: ignore
67+
except StopIteration as e:
68+
raise ValueError(f"Effect `{effect}` not found") from e
69+
data = utils.pack_string(effect.name) # type: ignore
70+
71+
self.send_packet(EffectPacketType.STOP_EFFECT, data)

openrgb/utils.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class PacketType(IntEnum):
7676
REQUEST_SAVE_PROFILE = 151
7777
REQUEST_LOAD_PROFILE = 152
7878
REQUEST_DELETE_PROFILE = 153
79+
REQUEST_PLUGIN_LIST = 200
80+
PLUGIN_SPECIFIC = 201
7981
RGBCONTROLLER_RESIZEZONE = 1000
8082
RGBCONTROLLER_UPDATELEDS = 1050
8183
RGBCONTROLLER_UPDATEZONELEDS = 1051
@@ -678,6 +680,36 @@ def unpack(cls, data: Iterator[int], version: int, *args) -> Profile:
678680
return cls(s)
679681

680682

683+
@dataclass
684+
class Plugin:
685+
'''
686+
687+
'''
688+
name: str
689+
description: str
690+
version: str
691+
id: int
692+
sdk_version: int
693+
694+
def pack(self):
695+
pass
696+
697+
@classmethod
698+
def unpack(cls, data: Iterator[int], version: int, *args) -> Plugin:
699+
name = parse_string(data)
700+
description = parse_string(data)
701+
version_str = parse_string(data)
702+
id = parse_var('i', data)
703+
sdk_version = parse_var('I', data)
704+
return cls(
705+
name,
706+
description,
707+
version_str,
708+
id,
709+
sdk_version
710+
)
711+
712+
681713
class RGBObject:
682714
'''
683715
A parent class that includes a few generic functions that use the

0 commit comments

Comments
 (0)