Accessing the device
The device serves a TCP server that can be connected to via WiFi:
Click knob button 5 times
This will either start an AP - SSID: serenspecs, Password: startstar
Or if you've already added your local Wifi credentials it will connect directly
The server can be reached on it's IP address, or via mDNS
AP = 192.168.4.1
Local WiFi = serenspecs.local
If your wifi doesn't support mDNS, then you'll need to get the IP directly from your router
The Server is accessible at port 1099
Chataigne is an excellent open-source-software that can handle these commands with an elegant GUI and timeline. If you don't know what you're doing, I would start there.
LED
Most Used, Basic Controls
Controlling the LEDs via TCP, all of the most used commands
led.fill(r, g, b)
Sync. Set all LEDs (left + right groups) to RGB and write to strip.
Parameters: r,g,b (int 0–255)
Example: led.fill(255, 100, 0)
led.fill_defaults()
Sync. Fill all LEDs with the current led.red/green/blue values and write.
Example: led.fill_defaults()
led.off()
Sync. Turn all LEDs off (sets RGB = 0 and writes).
Example: led.off()
led.start_flick_duty(freq, duty)
Sync. Start flick with freq and set flick_duty and duty_light accordingly.
Parameters: freq (float), duty (float 0.0–1.0)
Example: led.start_flick_duty(12.0, 0.6)
led.duty_freq_col(duty)
Sync. Map duty to a color increment, update color and map duty to frequency, then start flick.
Parameters: duty (float 0.0–1.0)
Example: led.duty_freq_col(0.8)
led.stop_flick()
Sync. Stop Timer(0) and turn LEDs off.
Example: led.stop_flick()
led.stop()
Sync. Stop any running xp mode task, clear led.mode, broadcast state change, stop flicks and turn LEDs off.
Example: led.stop()
led.duty_light(duty)
Sync. Set internal duty (0.0–1.0) and scale color channels according to led.max_duty and led.col_active. Broadcasts real duty to config_server if set.
Parameters: duty (float 0.0–1.0)
Example: led.duty_light(0.9)
Experience Commands
Where possible, I recommend to wrap your experience program inside the start mode wrapper, as it will then effortlessley handle closing any open modes.
led.start_mode(mode)
Sync. Start an async mode (callable coroutine) as a Task. If led.loop exists it uses that loop; otherwise it uses asyncio.create_task.
Parameters: mode (coroutine object)
Example: led.start_mode(led.xp_manual())
There are only 2 main modes, manual and custom. Manual is designed for on-device user-control, and custom means you can create any kind of experience with a few variables.
async led.xp_manual()
Async. Built-in "experience" manual mode: set rot modes, set duty to 1.0 and run flick for led.duration seconds. Broadcasts active state.
Example: await led.xp_manual() or led.start_mode(led.xp_manual())
async led.xp_custom(
min_freq=8,
max_freq=12,
min_duty=0.5,
max_duty=1.0,
duration=None,
col=None,
ran=None,
bilateral=0)
min_freq=8,
max_freq=12,
min_duty=0.5,
max_duty=1.0,
duration=None,
col=None,
ran=None,
bilateral=0)
min_freq = Minimum frequency of the experience. 3-40Hz
max_freq = Maximum frequency within the experience. 3-40Hz
max_freq must be bigger than min_freq
min_duty = Lowest brightness within the experience. 0.1-1.0
max_duty = Maximum brightness within the experience. 0.1-1.0
duration = Length of experience in seconds
col = Colour mode:
0 = Red Only
1 = White Only
2 = Colour Mode: At each new frequency change, the device will choose a random colour
ran = Kaos Dial: Rate of change of frequency. 1-9
Number of seconds in between changing frequencies. 1 makes for a very rapid change and is better suited for stimulating purposes, 9 means for much longer holds on each frequency for a more relaxing experience
bilateral = Bilateral situmation mode:
0 = No Bilateral Mode
1 = Bilateral v1: alternate light between left and right eye. The flicker is always on in one of the eyes.
2 = Bilateral v2: alternate light between eyes with additional darkness count. Half of the time there is no light in either eye, and the other half of the time, only one eye has light
The long tail of other commands
These commands are lower-level, meaning they are unlikely to be useful in most use cases, but having them available does open some doors to the very adventurous of you, who want to play with more features.
led.led_formation()
Sync. Recomputes left/right LED index groups based on led.num_leds and sets led.variant.
Use when you change num_leds at runtime.
Example: led.led_formation()
led.fill_left()
Sync. Set left group LEDs to current color; right group off, then write.
Example: led.fill_left()
led.fill_right()
Sync. Set right group LEDs to current color; left group off, then write.
Example: led.fill_right()
async led.start_flick(freq)
Async (wrapper). Starts the periodic flick behaviour at frequency freq by calling start_flick_normal.
Parameters: freq (float)
Example: await led.start_flick(8.0)
led.start_flick_normal(freq)
Sync. Configure Timer(0) for periodic callbacks (uses led.flick_mode) at computed period from freq and duty_range and sets LED duty state.
Parameters: freq (float)
Side effects: calls led.duty_light, starts Timer, may broadcast to config_server.
Example: led.start_flick_normal(10.0)
led.duty_freq(duty, freq)
Sync. Set duty_light(duty) and start flick at freq.
Parameters: duty (float), freq (float)
Example: led.duty_freq(0.7, 9.0)
flick callbacks (these are Timer callbacks; do not normally call directly)
led.flick(timer)
Sync callback. Standard duty-based on/off cycle.
led.bilateral_v1(timer)
Sync callback. Alternates left/right based on flick_state.
led.bilateral_v2(timer)
Sync callback. Another bilateral pattern using flick_state phases.
Note: These take a Timer object argument and are typically invoked via Timer when you call start_flick_normal.
led.update_col(col)
Sync. Set led.col (0–255), update internal RGB using wheel(col), and apply current duty by calling duty_light.
Parameters: col (int 0–255)
Example: led.update_col(128)
led.update_active_col(mode)
Sync. Set which color mode to apply for duty control. Accepts an int index into col_options or the mode string.
Parameters: mode (int index or str: 'red' | 'white' | 'col')
Example: led.update_active_col(2) # 'col' mode
led.set_white(bit)
Sync. Set red, green, blue values to a single white brightness (0–255).
Parameters: bit (int)
Example: led.set_white(200)
led.random_col()
Sync. Pick a random RGB color (0–255 each), set internal rgb and apply duty_light.
Example: led.random_col()
async led.freq_transition(new_freq, duration=500, start_freq=None)
Async. Smoothly transitions from current freq (or start_freq) to new_freq over duration (ms). Internally calls start_flick repeatedly.
Parameters: new_freq (float), duration (int ms), start_freq (float | None)
Example: await led.freq_transition(15.0, duration=1000)
async led.freq_transition_dur(new_freq=10, new_duty=1.0, duration=500, start_freq=None)
Async. Stepwise transition of freq and duty; sleeps 500ms between steps by design.
Parameters: new_freq (float), new_duty (float), duration (int ms), start_freq (float | None)
Example: await led.freq_transition_dur(12, 0.8, duration=2000)
led.toggle_bilateral()
Sync. Cycle the flick mode through the defined flick_modes, restart flick at current freq.
Example: led.toggle_bilateral()
led.change_bilateral(mode)
Sync. Set flick mode by index (0=flick,1=bilateral_v1,2=bilateral_v2) and restart flick; broadcasts state for the selected mode.
Parameters: mode (int)
Example: led.change_bilateral(1)
async led.pulse(dur=3, col=1)
Async. Pulses LEDs for dur seconds using 'col' color index (0–255). Temporarily sets color mode to 'col' then restores previous col_active. Writes continuously while pulsing.
Parameters: dur (int seconds), col (int 0–255)
Example: await led.pulse(5, col=120)
led.wheel(pos)
Sync. Color wheel helper. Returns a tuple (r,g,b) for 0–255 wheel positions.
Parameters: pos (int)
Returns: (r,g,b) tuple
Example: rgb = led.wheel(50)
led.get_method_titles_and_xp_methods()
Sync. Returns a tuple of method names starting with 'xp' on the instance (strings).
Example: xp_list = led.get_method_titles_and_xp_methods()
led.broadcast_state(button_ID, state)
Sync. If config_server exists, broadcast a UI/state update for the given button_ID and boolean/integer state.
Parameters: button_ID (str), state (int|bool)
Example: led.broadcast_state('experience_mode', True)
led.set_dependencies(rot=None, battery=None, loop=None)
Sync. Inject or update runtime dependencies (rot controller, battery reader, asyncio loop).
Example: led.set_dependencies(rot=my_rot, battery=my_batt, loop=asyncio_loop)
led.set_max_duty(duty)
Sync. Update led.max_duty and mirror value to rot.values["max_duty"] if rot present.
Parameters: duty (float)
Example: led.set_max_duty(0.9)
led.set_rot_modes(modes)
Sync. Set led.modes tuple and update rot.state and rot.mode_selector[0] if rot exists. Used to expose which controls are active.
Parameters: modes (tuple)
Example: led.set_rot_modes(('max_duty', 'freq'))
async led.battery_display()
Async. Reads battery value (if battery dependency present), maps it to a color, runs pulse to show battery then restores duty and stops flick.
Example: await led.battery_display()