First working branch
This commit is contained in:
parent
b939420c65
commit
ab8f7df848
|
@ -15,14 +15,14 @@ Configuration is done through a JSON file named `config.json` in the same direct
|
||||||
"code": "<your shocker's code>",
|
"code": "<your shocker's code>",
|
||||||
"comboA": 100,
|
"comboA": 100,
|
||||||
"comboB": 300,
|
"comboB": 300,
|
||||||
"comboABreak": ["vibrate", 20, 1.0],
|
"comboABreak": ["vibrate", 1.0, 20],
|
||||||
"comboBBreak": ["shock": 5, 1.0],
|
"comboBBreak": ["shock", 1.0, 5],
|
||||||
"fail": ["shock", 10, 2.0]
|
"fail": ["shock", 2.0, 10]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The values for combo breaks and fail is an array.
|
The values for combo breaks and fail is an array.
|
||||||
|
|
||||||
1. `vibrate`, `shock`, and `beep`.
|
1. `vibrate`, `shock`, and `beep` (if action is `beep`, intensity is ignored)
|
||||||
2. Intensity
|
2. Duration
|
||||||
3. Duration
|
3. Intensity
|
||||||
|
|
|
@ -21,14 +21,24 @@ class BaseData:
|
||||||
async with websockets.connect(
|
async with websockets.connect(
|
||||||
'ws://{}:{}{}'.format(self.host, self.port, self.path)) as websocket:
|
'ws://{}:{}{}'.format(self.host, self.port, self.path)) as websocket:
|
||||||
async for message in websocket:
|
async for message in websocket:
|
||||||
await self._handle(message)
|
logger.debug(message)
|
||||||
|
if message:
|
||||||
|
self._handle(json.loads(message))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.critical(e)
|
logger.critical(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
class LiveData(BaseData):
|
class LiveData(BaseData):
|
||||||
TRIGGERS = enum.Enum('Triggers',
|
TRIGGERS = enum.Enum('Triggers',
|
||||||
['Unknown', 'TimerElapsed', 'NoteMissed', 'EnergyChange', 'ScoreChange'])
|
['Unknown', 'TimerElapsed', 'NoteMissed', 'EnergyChange', 'ScoreChange'])
|
||||||
|
TRIGGERS_INVERSE = [
|
||||||
|
TRIGGERS.Unknown,
|
||||||
|
TRIGGERS.TimerElapsed,
|
||||||
|
TRIGGERS.NoteMissed,
|
||||||
|
TRIGGERS.EnergyChange,
|
||||||
|
TRIGGERS.ScoreChange,
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, host: str = '127.0.0.1', port: int = 2946,
|
def __init__(self, host: str = '127.0.0.1', port: int = 2946,
|
||||||
path: str = '/BSDataPuller/LiveData'):
|
path: str = '/BSDataPuller/LiveData'):
|
||||||
|
@ -41,8 +51,10 @@ class LiveData(BaseData):
|
||||||
self.events[event].append(function)
|
self.events[event].append(function)
|
||||||
|
|
||||||
def _handle(self, message: str):
|
def _handle(self, message: str):
|
||||||
for event in events[TRIGGERS(message['EventTrigger']).name]:
|
invert = self.TRIGGERS_INVERSE[message['EventTrigger']].name
|
||||||
event(message)
|
if invert in self.events:
|
||||||
|
for event in self.events[invert]:
|
||||||
|
event(message)
|
||||||
|
|
||||||
|
|
||||||
class MapData(BaseData):
|
class MapData(BaseData):
|
||||||
|
|
|
@ -66,8 +66,8 @@ python-versions = ">=3.7"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.9"
|
||||||
content-hash = "5a734eed883538e1b3f3ec6d75d33bfcf056ecd3a28311b0e975c847bb268ce2"
|
content-hash = "a1bceaf7911989ee719f409e1eb231de0d8e0d8ea8faebf994892b0f12f651cd"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
certifi = [
|
certifi = [
|
||||||
|
|
|
@ -7,7 +7,7 @@ license = "MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.10"
|
python = "^3.9"
|
||||||
requests = "^2.28.1"
|
requests = "^2.28.1"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from threading import Timer
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -15,16 +14,13 @@ class PiShock:
|
||||||
ACTIONS = Enum('ACTIONS', ['shock', 'vibrate', 'beep'])
|
ACTIONS = Enum('ACTIONS', ['shock', 'vibrate', 'beep'])
|
||||||
|
|
||||||
def __init__(self, username: str, api_key: str, code: str, name: str = '',
|
def __init__(self, username: str, api_key: str, code: str, name: str = '',
|
||||||
max_intensity: int = 50, max_duration: int = 5, throttle: int = 5):
|
max_intensity: int = 50, max_duration: int = 5):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
self.code = code
|
self.code = code
|
||||||
self.name = name if name else self.NAME
|
self.name = name if name else self.NAME
|
||||||
self.max_intensity = max_intensity
|
self.max_intensity = max_intensity
|
||||||
self.max_duration = max_duration
|
self.max_duration = max_duration
|
||||||
self.throttle = throttle
|
|
||||||
self.throttled = False
|
|
||||||
self.throttle_timer = None
|
|
||||||
|
|
||||||
def shock(self, duration: int = 1, intensity: int = 5):
|
def shock(self, duration: int = 1, intensity: int = 5):
|
||||||
self._act(self.ACTIONS.shock, duration, intensity)
|
self._act(self.ACTIONS.shock, duration, intensity)
|
||||||
|
@ -42,9 +38,6 @@ class PiShock:
|
||||||
logger.warning('Action not found: {}', e)
|
logger.warning('Action not found: {}', e)
|
||||||
|
|
||||||
def _act(self, op: str, duration: int = 1, intensity: int = 5):
|
def _act(self, op: str, duration: int = 1, intensity: int = 5):
|
||||||
if op == self.ACTIONS.shock and self.throttled:
|
|
||||||
logger.error('Request throttled')
|
|
||||||
return
|
|
||||||
if duration > self.max_duration:
|
if duration > self.max_duration:
|
||||||
logger.error('%d is greater than maximum allowed duration, %d',
|
logger.error('%d is greater than maximum allowed duration, %d',
|
||||||
duration, self.max_duration)
|
duration, self.max_duration)
|
||||||
|
@ -71,7 +64,3 @@ class PiShock:
|
||||||
logger.debug(res.text)
|
logger.debug(res.text)
|
||||||
if res.status_code != 200 or res.text != 'Operation Succeeded.':
|
if res.status_code != 200 or res.text != 'Operation Succeeded.':
|
||||||
logger.warning('Error communicating with shocker: %s', res.text)
|
logger.warning('Error communicating with shocker: %s', res.text)
|
||||||
|
|
||||||
if op == self.ACTIONS.shock:
|
|
||||||
self.throttled = True
|
|
||||||
self.throttle_timer = Timer(self.throttle, lambda: self.throttled == False)
|
|
||||||
|
|
|
@ -10,72 +10,75 @@ from datapuller import (
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
playing = False
|
|
||||||
combo_level = None
|
|
||||||
shocker = None
|
|
||||||
failed = False
|
|
||||||
events = None
|
|
||||||
|
|
||||||
|
|
||||||
class Events:
|
class Events:
|
||||||
combo_A = 100
|
combo_A = 100
|
||||||
combo_B = 300
|
combo_B = 300
|
||||||
|
|
||||||
combo_A_break = ['vibrate', 20, 0.5]
|
combo_A_break = ['vibrate', 0.5, 20]
|
||||||
combo_B_break = ['shock', 5, 0.3]
|
combo_B_break = ['shock', 0.3, 5]
|
||||||
|
|
||||||
fail = ['shock', 10, 1]
|
fail = ['shock', 1, 10]
|
||||||
|
|
||||||
def __init__(self, config):
|
playing = False
|
||||||
|
combo_level = None
|
||||||
|
map_failed = False
|
||||||
|
|
||||||
|
def __init__(self, config, shocker):
|
||||||
self.combo_A = config['comboA']
|
self.combo_A = config['comboA']
|
||||||
self.combo_B = config['comboB']
|
self.combo_B = config['comboB']
|
||||||
self.combo_A_break = config['comboABreak']
|
self.combo_A_break = config['comboABreak']
|
||||||
self.combo_B_break = config['comboBBreak']
|
self.combo_B_break = config['comboBBreak']
|
||||||
self.fail = config['fail']
|
self.fail = config['fail']
|
||||||
|
self.shocker = shocker
|
||||||
|
|
||||||
|
def update_combo(self, message):
|
||||||
|
combo = message['Combo']
|
||||||
|
health = message['PlayerHealth']
|
||||||
|
if not self.map_failed and health == 0:
|
||||||
|
logger.info('Failed the map!')
|
||||||
|
self.shocker.act(*self.fail)
|
||||||
|
self.map_failed = True
|
||||||
|
self.combo_level = None
|
||||||
|
if combo > self.combo_A:
|
||||||
|
self.combo_level = 'A'
|
||||||
|
if combo > self.combo_B:
|
||||||
|
self.combo_level = 'B'
|
||||||
|
|
||||||
|
def break_combo(self, message):
|
||||||
|
if self.combo_level == 'A':
|
||||||
|
logger.info('Broke combo A!')
|
||||||
|
self.shocker.act(*self.combo_A_break)
|
||||||
|
if self.combo_level == 'B':
|
||||||
|
logger.info('Broke combo B!')
|
||||||
|
self.shocker.act(*self.combo_B_break)
|
||||||
|
self.combo_level = None
|
||||||
|
|
||||||
|
def tick(self, message):
|
||||||
|
logger.info('Tick')
|
||||||
|
if self.playing and message['TimeElapsed'] == 0:
|
||||||
|
logger.info('Stopping map...')
|
||||||
|
self.map_failed = False
|
||||||
|
self.combo = 0
|
||||||
|
self.playing = False
|
||||||
|
elif not self.playing and message['TimeElapsed'] != 0:
|
||||||
|
logger.info('Starting map...')
|
||||||
|
self.playing = True
|
||||||
|
|
||||||
|
|
||||||
|
async def main(config):
|
||||||
def update_combo(message):
|
logger.info('Initializing...')
|
||||||
combo = message['Combo']
|
shocker = PiShock(config['username'], config['apiKey'], config['code'], name='ZapSaber')
|
||||||
if combo > events.combo_A:
|
#shocker.beep()
|
||||||
combo_level = 'A'
|
events = Events(config, shocker)
|
||||||
if combo > events.combo_B:
|
|
||||||
combo_level = 'B'
|
|
||||||
|
|
||||||
|
|
||||||
def break_combo(message):
|
|
||||||
if combo_level == 'A':
|
|
||||||
logger.info('Broke combo A!')
|
|
||||||
shocker.act(*events.combo_A_break)
|
|
||||||
if combo_level == 'B':
|
|
||||||
logger.info('Broke combo B!')
|
|
||||||
shocker.act(*events.combo_B_break)
|
|
||||||
combo_level = None
|
|
||||||
|
|
||||||
|
|
||||||
def map_update(message):
|
|
||||||
if map_failed and not message['LevelFailed']:
|
|
||||||
map_failed = false
|
|
||||||
elif not map_failed and message['LevelFailed']:
|
|
||||||
logger.info('Failed the map!')
|
|
||||||
shocker.act(*events.fail)
|
|
||||||
map_failed = true
|
|
||||||
combo_level = None
|
|
||||||
|
|
||||||
|
|
||||||
async def main(username, api_key, code):
|
|
||||||
logging.info('Initializing...')
|
|
||||||
shocker = PiShock(username, api_key, code, name='ZapSaber')
|
|
||||||
shocker.beep()
|
|
||||||
|
|
||||||
livedata = LiveData()
|
livedata = LiveData()
|
||||||
mapdata = MapData()
|
|
||||||
|
|
||||||
livedata.on('ScoreChange', update_combo)
|
livedata.on('ScoreChange', events.update_combo)
|
||||||
livedata.on('NoteMissed', break_combo)
|
livedata.on('NoteMissed', events.break_combo)
|
||||||
mapdata.on_update(map_update)
|
livedata.on('TimerElapsed', events.tick)
|
||||||
|
|
||||||
await livedata.listen()
|
await livedata.listen()
|
||||||
await mapdata.listen()
|
await mapdata.listen()
|
||||||
|
@ -85,5 +88,4 @@ async def main(username, api_key, code):
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
with open('config.json') as config_file:
|
with open('config.json') as config_file:
|
||||||
config = json.load(config_file)
|
config = json.load(config_file)
|
||||||
events = Events(config)
|
asyncio.run(main(config))
|
||||||
asyncio.run(main(config['username'], config['apiKey'], config['code']))
|
|
||||||
|
|
Loading…
Reference in New Issue