Paket Üretimi
MacroStick’e core_payload endpoint’i üzerinden gönderilen her komut tam olarak 16 byte’lık sabit uzunlukta bir pakettir. Bu sayfa komut yapısını, doğrulama değerinin nasıl hesaplandığını ve yaygın komut türleri için hazır örnekleri kapsar.
Temel Yardımcı Sınıf
Aşağıdaki sınıfı doğrudan projelerinizde kullanabilirsiniz. Sayfanın geri kalanındaki tüm örnekler bu sınıf üzerine kuruludur.
import struct
import requests
class MacroStick:
"""MacroStick HTTP API istemcisi."""
SYNC = 0xA5
KEYBOARD = 0x02
MOUSE = 0x03
GAMEPAD = 0x04
CONSUMER = 0x05
SYSTEM_CONTROL = 0x06
FLAG_DISABLE_AUTO_RELEASE = 0x01
def __init__(self, host: str = "192.168.7.1", username: str = "", password: str = ""):
self.base_url = f"http://{host}/api/core_payload"
self.auth = (username, password)
self._queue: list[bytes] = []
# ------------------------------------------------------------------ #
# Paket oluşturma #
# ------------------------------------------------------------------ #
@staticmethod
def _crc8(data: bytes) -> int:
crc = 0x00
for byte in data:
crc ^= byte
for _ in range(8):
crc = ((crc << 1) ^ 0x07) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF
return crc
@classmethod
def _build(cls, type_byte: int, command_data: bytes, flags: int = 0x00) -> bytes:
assert len(command_data) == 12
crc_input = bytes([cls.SYNC, type_byte]) + command_data + bytes([flags])
checksum = cls._crc8(crc_input)
return bytes([cls.SYNC, type_byte]) + command_data + bytes([checksum, flags])
# ------------------------------------------------------------------ #
# Toplu gönderim #
# ------------------------------------------------------------------ #
def send(self, *packets: bytes) -> int:
"""
1–10 paket alır, tek HTTP isteğiyle gönderir.
Dönüş değeri: HTTP durum kodu.
"""
assert 1 <= len(packets) <= 10
payload = b"".join(packets).hex()
resp = requests.post(self.base_url, auth=self.auth, json={"payload": payload})
return resp.status_code
def queue(self, *packets: bytes):
"""Paketi kuyruğa ekle; flush() ile toplu gönder."""
self._queue.extend(packets)
def flush(self) -> list[int]:
"""Kuyruktaki tüm paketleri 10'arlı gruplar halinde gönderir."""
codes = []
while self._queue:
batch = self._queue[:10]
self._queue = self._queue[10:]
codes.append(self.send(*batch))
return codes
# ------------------------------------------------------------------ #
# Klavye #
# ------------------------------------------------------------------ #
@classmethod
def key_press(cls, keycode: int, modifier: int = 0x00, flags: int = 0x00) -> bytes:
data = bytes([modifier, 0x00, keycode, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
return cls._build(cls.KEYBOARD, data, flags)
@classmethod
def key_combo(cls, keycodes: list[int], modifier: int = 0x00, flags: int = 0x00) -> bytes:
"""En fazla 6 tuşu aynı anda basılı tut."""
assert len(keycodes) <= 6
padded = (keycodes + [0x00] * 6)[:6]
data = bytes([modifier, 0x00] + padded + [0x00, 0x00, 0x00, 0x00])
return cls._build(cls.KEYBOARD, data, flags)
@classmethod
def key_release(cls) -> bytes:
return cls._build(cls.KEYBOARD, bytes(12))
# ------------------------------------------------------------------ #
# Fare #
# ------------------------------------------------------------------ #
@classmethod
def mouse_move(cls, x: int, y: int) -> bytes:
data = struct.pack("bbbbb7x", 0, x, y, 0, 0)
return cls._build(cls.MOUSE, data)
@classmethod
def mouse_button(cls, buttons: int) -> bytes:
data = bytes([buttons]) + bytes(11)
return cls._build(cls.MOUSE, data)
@classmethod
def mouse_scroll(cls, wheel: int, pan: int = 0) -> bytes:
data = struct.pack("bbbbb7x", 0, 0, 0, wheel, pan)
return cls._build(cls.MOUSE, data)
# ------------------------------------------------------------------ #
# Consumer #
# ------------------------------------------------------------------ #
@classmethod
def consumer_key(cls, usage: int) -> bytes:
data = struct.pack("<H10x", usage)
return cls._build(cls.CONSUMER, data)
# ------------------------------------------------------------------ #
# Gamepad #
# ------------------------------------------------------------------ #
@classmethod
def gamepad(cls, x=0, y=0, z=0, rx=0, ry=0, rz=0, hat=0, buttons=0) -> bytes:
btn_bytes = struct.pack("<I", buttons) # 4 byte, little-endian
data = struct.pack("bbbbbbb", x, y, z, rx, ry, rz, hat) + btn_bytes + b"\x00"
return cls._build(cls.GAMEPAD, data)
# ------------------------------------------------------------------ #
# System Control #
# ------------------------------------------------------------------ #
@classmethod
def system_control(cls, usage: int) -> bytes:
data = bytes([usage]) + bytes(11)
return cls._build(cls.SYSTEM_CONTROL, data)
Paket Yapısı
Her komut tam olarak 16 byte’tır; daha kısa veya uzun paketler geçersiz sayılır. Endpoint’e gönderirken bu 16 byte’lık paket hex string olarak yazılır; bu nedenle tek paket 32 hex karakterdir.
| Konum | Boyut | Alan | Açıklama |
|---|---|---|---|
| 0 | 1 | sync | Her zaman 0xA5 — paket başlangıç işareti |
| 1 | 1 | type | Komut türü |
| 2–13 | 12 | commandData | Türe özgü veri |
| 14 | 1 | checksum | CRC-8 doğrulama değeri |
| 15 | 1 | flags | Davranış bayrakları |
Komut Türleri
| Değer | Tür | Kullanım |
|---|---|---|
0x02 | KEYBOARD | Klavye tuşu |
0x03 | MOUSE | Fare hareketi ve tıklama |
0x04 | GAMEPAD | Oyun kumandası |
0x05 | CONSUMER | Ses, medya, parlaklık |
0x06 | SYSTEM_CONTROL | Güç, uyku, uyanış |
Flags
| Bit | Değer | Anlamı |
|---|---|---|
| 0 | 0x01 | DISABLE_AUTO_RELEASE — Bu komut için otomatik bırakma devre dışı |
Otomatik bırakma aktifken cihaz belirli bir süre sonra tüm tuşları otomatik serbest bırakır. Bu bayrağı 0x01 yaparsanız bırakma komutu siz gönderene kadar tuş basılı kalır. Uzun süreli basılı tutma senaryolarında bu bayrağı kullanın.
Endpoint ile Gönderim
core_payload endpoint’i aynı istekte en fazla 10 paketi taşıyacak şekilde kullanılmalıdır. 10 paketten fazla komut gönderecekseniz paketleri gruplara ayırın.
ms = MacroStick(username="KULLANICI_ADI", password="SIFRE")
ms.send(ms.key_press(0x04), ms.key_release())
Paket formatı geçersizse ilgili paket çalıştırılmaz. HTTP yanıt kodu istek seviyesini gösterir; her paketin ayrı başarı bilgisini döndürmez.
CRC-8 Hesaplama
checksum şu alanların sırayla birleştirilmesiyle hesaplanır: sync (1 byte) + type (1 byte) + commandData (12 byte) + flags (1 byte). Toplamda 15 byte girdi, checksum baytının kendisi dahil değil.
Algoritma: CRC-8, polinom 0x07, başlangıç değeri 0x00.
def crc8(data: bytes) -> int:
crc = 0x00
for byte in data:
crc ^= byte # Geçerli baytı XOR ile karıştır
for _ in range(8): # 8 bit işle
if crc & 0x80: # MSB set ise
crc = ((crc << 1) ^ 0x07) & 0xFF
else:
crc = (crc << 1) & 0xFF
return crc
Adım adım örnek — sync=0xA5, type=0x02, commandData=bytes(12), flags=0x00:
data = bytes([0xA5, 0x02]) + bytes(12) + bytes([0x00])
checksum = crc8(data)
print(hex(checksum)) # 0xe1
Klavye Komutları (KEYBOARD — 0x02)
commandData Düzeni
| Offset | Alan | Boyut | Açıklama |
|---|---|---|---|
| 0 | modifier | 1 | Modifier tuş kombinasyonu |
| 1 | reserved | 1 | 0x00 |
| 2–7 | keycodes[6] | 6 | Aynı anda basılan tuşların HID kodları |
| 8–11 | reserved | 4 | 0x00 |
Modifier Bitleri
Her bit bir modifier tuşunu temsil eder. Birden fazla modifier için bitleri OR’layın.
| Bit | Değer | Tuş |
|---|---|---|
| 0 | 0x01 | Sol Ctrl |
| 1 | 0x02 | Sol Shift |
| 2 | 0x04 | Sol Alt |
| 3 | 0x08 | Sol GUI (Win / Cmd) |
| 4 | 0x10 | Sağ Ctrl |
| 5 | 0x20 | Sağ Shift |
| 6 | 0x40 | Sağ Alt (AltGr) |
| 7 | 0x80 | Sağ GUI |
Örnekler:
- Sol Shift →
0x02 - Sol Ctrl + Sol Alt →
0x01 | 0x04=0x05 - Sol Ctrl + Sol Shift →
0x01 | 0x02=0x03 - Sol Ctrl + Sol Shift + Sol Alt →
0x07
USB HID Klavye Kodları
Harfler (büyük/küçük harf modifier ile belirlenir, kod aynıdır)
| Tuş | Kod | Tuş | Kod | Tuş | Kod |
|---|---|---|---|---|---|
| A | 0x04 | J | 0x0D | S | 0x16 |
| B | 0x05 | K | 0x0E | T | 0x17 |
| C | 0x06 | L | 0x0F | U | 0x18 |
| D | 0x07 | M | 0x10 | V | 0x19 |
| E | 0x08 | N | 0x11 | W | 0x1A |
| F | 0x09 | O | 0x12 | X | 0x1B |
| G | 0x0A | P | 0x13 | Y | 0x1C |
| H | 0x0B | Q | 0x14 | Z | 0x1D |
| I | 0x0C | R | 0x15 |
Rakamlar ve Semboller
| Tuş | Kod | Tuş | Kod |
|---|---|---|---|
| 1 | 0x1E | 6 | 0x23 |
| 2 | 0x1F | 7 | 0x24 |
| 3 | 0x20 | 8 | 0x25 |
| 4 | 0x21 | 9 | 0x26 |
| 5 | 0x22 | 0 | 0x27 |
Özel Tuşlar
| Tuş | Kod | Tuş | Kod |
|---|---|---|---|
| Enter | 0x28 | F1 | 0x3A |
| Escape | 0x29 | F2 | 0x3B |
| Backspace | 0x2A | F3 | 0x3C |
| Tab | 0x2B | F4 | 0x3D |
| Boşluk | 0x2C | F5 | 0x3E |
| Caps Lock | 0x39 | F6 | 0x3F |
| Print Screen | 0x46 | F7 | 0x40 |
| Insert | 0x49 | F8 | 0x41 |
| Home | 0x4A | F9 | 0x42 |
| Page Up | 0x4B | F10 | 0x43 |
| Delete | 0x4C | F11 | 0x44 |
| End | 0x4D | F12 | 0x45 |
| Page Down | 0x4E | ||
| Sağ ok | 0x4F | Yukarı ok | 0x52 |
| Sol ok | 0x50 | ||
| Aşağı ok | 0x51 |
Klavye Örnekleri
Tek tuş: “A” basma ve bırakma
ms = MacroStick(username="KULLANICI_ADI", password="SIFRE")
press = ms.key_press(0x04) # A
release = ms.key_release()
ms.send(press, release)
Büyük harf: “A” (Shift + a)
press = ms.key_press(0x04, modifier=0x02) # Sol Shift + A
release = ms.key_release()
ms.send(press, release)
Kısayol: Ctrl+C
press = ms.key_press(0x06, modifier=0x01) # Sol Ctrl + C
release = ms.key_release()
ms.send(press, release)
Kısayol: Ctrl+Alt+Delete
press = ms.key_combo([0x4C], modifier=0x05) # Ctrl+Alt + Delete (0x4C)
release = ms.key_release()
ms.send(press, release)
Windows Çalıştır kutusu açma ve komut çalıştırma
Win+R açar, notepad yazar, Enter’a basar:
import time
ms = MacroStick(username="KULLANICI_ADI", password="SIFRE")
KEY = {
"enter": 0x28, "n": 0x11, "o": 0x12, "t": 0x17,
"e": 0x08, "p": 0x13, "a": 0x04, "d": 0x07
}
WIN_R = ms.key_combo([0x15], modifier=0x08) # GUI + R
ms.send(WIN_R, ms.key_release())
time.sleep(0.5)
for char, code in [("n", KEY["n"]), ("o", KEY["o"]), ("t", KEY["t"]),
("e", KEY["e"]), ("p", KEY["p"]), ("a", KEY["a"]),
("d", KEY["d"])]:
ms.send(ms.key_press(code), ms.key_release())
time.sleep(0.05)
ms.send(ms.key_press(KEY["enter"]), ms.key_release())
Metin yazdırma (batch ile)
10’dan uzun metinler için queue + flush kullanın:
import time
ms = MacroStick(username="KULLANICI_ADI", password="SIFRE")
# "hello" → h=0x0B, e=0x08, l=0x0F, l=0x0F, o=0x12
for code in [0x0B, 0x08, 0x0F, 0x0F, 0x12]:
ms.queue(ms.key_press(code), ms.key_release())
ms.flush()
Aynı anda birden fazla tuş (gaming combo)
# W + Shift (sprint) — oyun senaryosu
press = ms.key_combo([0x1A], modifier=0x02) # Shift + W
release = ms.key_release()
ms.send(press, release)
Fare Komutları (MOUSE — 0x03)
commandData Düzeni
| Offset | Alan | Tür | Açıklama |
|---|---|---|---|
| 0 | buttons | uint8 | Bit 0=Sol, Bit 1=Sağ, Bit 2=Orta |
| 1 | x | int8 | Yatay hareket: -128 (sol) … +127 (sağ) |
| 2 | y | int8 | Dikey hareket: -128 (yukarı) … +127 (aşağı) |
| 3 | wheel | int8 | Dikey tekerlek: + yukarı, - aşağı |
| 4 | pan | int8 | Yatay kaydırma: + sağ, - sol |
| 5–11 | reserved | — | 0x00 |
Fare hareketi görecelidir; her komut mevcut konumdan offset ekler. 127 pikselden fazla hareket için birden fazla komut gönderin.
Fare Örnekleri
Sağa 50 piksel hareket
ms.send(ms.mouse_move(50, 0))
Büyük hareket — 300 piksel sağa (birden fazla komut)
# 127 piksel sınırını aşmak için parçalara bölün
import time
remaining = 300
while remaining > 0:
step = min(remaining, 127)
ms.send(ms.mouse_move(step, 0))
remaining -= step
time.sleep(0.01)
Sol tık
press = ms.mouse_button(0x01)
release = ms.mouse_button(0x00)
ms.send(press, release)
Sağ tık
press = ms.mouse_button(0x02)
release = ms.mouse_button(0x00)
ms.send(press, release)
Çift tık
import time
click = [ms.mouse_button(0x01), ms.mouse_button(0x00)]
ms.send(*click)
time.sleep(0.05)
ms.send(*click)
Sürükleme (drag) — sol tık basılı tutarak hareket
ms.send(ms.mouse_button(0x01)) # Sol tık basılı
ms.send(ms.mouse_move(100, 0)) # 100 piksel sağa sürükle
ms.send(ms.mouse_button(0x00)) # Bırak
Tekerlek kaydırma
ms.send(ms.mouse_scroll(3)) # Yukarı kaydır
ms.send(ms.mouse_scroll(-3)) # Aşağı kaydır
Consumer Komutları (CONSUMER — 0x05)
Ses düzeyi, medya kontrolü, parlaklık gibi özel tuşlar için kullanılır.
commandData Düzeni
| Offset | Alan | Tür | Açıklama |
|---|---|---|---|
| 0–1 | usage | uint16 LE | USB HID Consumer usage kodu |
| 2–11 | reserved | — | 0x00 |
Sık Kullanılan Consumer Kodları
| İşlev | Kod |
|---|---|
| Ses artır | 0x00E9 |
| Ses kıs | 0x00EA |
| Sessiz aç/kapat | 0x00E2 |
| Oynat / Duraklat | 0x00CD |
| Dur | 0x00B7 |
| Sonraki parça | 0x00B5 |
| Önceki parça | 0x00B6 |
| Baştan başlat | 0x00B4 |
| Hızlı ileri | 0x00B3 |
| Geri sar | 0x00B2 |
| Parlaklık artır | 0x006F |
| Parlaklık azalt | 0x0070 |
| Hesap makinesi | 0x0192 |
| Tarayıcı ana sayfa | 0x0223 |
Consumer Örnekleri
Ses artırma (3 kez)
import time
for _ in range(3):
ms.send(
ms.consumer_key(0x00E9), # Ses artır
ms.consumer_key(0x0000) # Bırak
)
time.sleep(0.1)
Oynat/Duraklat
ms.send(
ms.consumer_key(0x00CD),
ms.consumer_key(0x0000)
)
Sonraki parça
ms.send(
ms.consumer_key(0x00B5),
ms.consumer_key(0x0000)
)
Gamepad Komutları (GAMEPAD — 0x04)
commandData Düzeni
| Offset | Alan | Tür | Açıklama |
|---|---|---|---|
| 0 | x | int8 | Sol analog: yatay (-128 sol … +127 sağ) |
| 1 | y | int8 | Sol analog: dikey (-128 yukarı … +127 aşağı) |
| 2 | z | int8 | Sağ analog: yatay |
| 3 | rx | int8 | Sağ analog: dikey |
| 4 | ry | int8 | Ekstra eksen |
| 5 | rz | int8 | Ekstra eksen |
| 6 | hat | int8 | D-Pad (0=serbest, 1=kuzey, 2=KB, 3=doğu…) |
| 7–10 | buttons[4] | uint32 LE | 32 bit düğme maskesi |
| 11 | reserved | — | 0x00 |
Gamepad Örnekleri
Sol analog çubuğu sağa it
ms.send(
ms.gamepad(x=100), # Sağa %78 (100/127)
ms.gamepad(x=0) # Serbest bırak
)
A düğmesine bas (bit 0)
ms.send(
ms.gamepad(buttons=0x01), # A basılı
ms.gamepad(buttons=0x00) # Bırak
)
D-Pad yukarı
ms.send(
ms.gamepad(hat=1), # Kuzey
ms.gamepad(hat=0) # Serbest
)
System Control Komutları (SYSTEM_CONTROL — 0x06)
Güç yönetimi komutları için kullanılır.
commandData Düzeni
| Offset | Alan | Tür | Açıklama |
|---|---|---|---|
| 0 | usage | uint8 | USB HID System Control usage kodu |
| 1–11 | reserved | — | 0x00 |
Sık Kullanılan Kodlar
| İşlev | Kod |
|---|---|
| Güç kapat | 0x81 |
| Uyku | 0x82 |
| Uyan | 0x83 |
Örnek: Uyku moduna al
ms.send(
ms.system_control(0x82), # Uyku
ms.system_control(0x00) # Bırak
)
Hata Durumları
| Durum | Sonuç |
|---|---|
Yanlış sync baytı (0xA5 değil) | Paket sessizce düşürülür |
| CRC-8 uyuşmazlığı | Paket sessizce düşürülür |
Geçersiz type değeri | Paket sessizce düşürülür |
payload 320 hex karakterden uzun | Payload’ı 10 paketlik isteklere bölün |
| Komut kuyruğu dolu | Paket sessizce düşürülür |
| Yanlış kimlik bilgisi | 401 Unauthorized |
Birden fazla paket içeren bir istekte geçerli paketler işlenmeye devam eder; geçersiz paketler yalnızca kendisi atlanır.
Zamanlama
MacroStick paketler arasına otomatik gecikme eklemez. Basış ve bırakma arasındaki süre tamamen gönderdiğiniz isteklerin sırasına ve arasına koyduğunuz gecikmelere bağlıdır.
- Tek tuş basışı: basma + bırakma komutunu aynı istekte gönderin.
- Tuş arası gecikme: birden fazla tuş arasına
time.sleep(0.05)ekleyin; bazı uygulamalar çok hızlı ardışık girişleri kaçırabilir. - Uzun basılı tutma:
FLAG_DISABLE_AUTO_RELEASEbayrağını0x01yapın ve bırakma komutunu istediğiniz süre sonra gönderin.
import time
# Shift tuşunu 2 saniye basılı tut
ms.send(ms.key_press(0x02, flags=MacroStick.FLAG_DISABLE_AUTO_RELEASE)) # Sol Shift
time.sleep(2)
ms.send(ms.key_release())