wled: Add extra control for wled effects
For the "Percent" preset the intensity controls the amount of the bar lit up 0-100 Work around issue when preset is re-enabled if sending intensity or speed while no preset is active Removed control endpoint Added missed toggle endpoint Transition time to 0 for control changes Signed-off-by: Richard Mitchell <richardjm+moonraker@gmail.com>
This commit is contained in:
parent
fcfa563d02
commit
56097a35ad
|
@ -1491,6 +1491,20 @@ gcode:
|
|||
state=True,
|
||||
preset=preset)}
|
||||
|
||||
[gcode_macro WLED_CONTROL]
|
||||
description: Control effect values and brightness
|
||||
gcode:
|
||||
{% set strip = params.STRIP|default('lights')|string %}
|
||||
{% set brightness = params.BRIGHTNESS|default(-1)|int %}
|
||||
{% set intensity = params.INTENSITY|default(-1)|int %}
|
||||
{% set speed = params.SPEED|default(-1)|int %}
|
||||
|
||||
{action_call_remote_method("set_wled_state",
|
||||
strip=strip,
|
||||
brightness=brightness,
|
||||
intensity=intensity,
|
||||
speed=speed)}
|
||||
|
||||
[gcode_macro WLED_OFF]
|
||||
description: Turn WLED strip off
|
||||
gcode:
|
||||
|
|
334
docs/web_api.md
334
docs/web_api.md
|
@ -3479,6 +3479,340 @@ An object containing power state for each requested device:
|
|||
"printer": "off"
|
||||
}
|
||||
```
|
||||
### WLED APIs
|
||||
The APIs for WLED are available when the `[wled]` component has been configured. For lower-level control of wled consider using the WLED [JOSN API](https://kno.wled.ge/interfaces/json-api/) directly.
|
||||
|
||||
#### Get strips
|
||||
HTTP request:
|
||||
```http
|
||||
GET /machine/wled/strips
|
||||
```
|
||||
JSON-RPC request:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method":"machine.wled.strips",
|
||||
"id": 7123
|
||||
}
|
||||
```
|
||||
Returns:
|
||||
|
||||
Strip information for all wled strips.
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"strips": {
|
||||
"lights": {
|
||||
"strip": "lights",
|
||||
"status": "on",
|
||||
"chain_count": 79,
|
||||
"preset": -1,
|
||||
"brightness": 255,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
},
|
||||
"desk": {
|
||||
"strip": "desk",
|
||||
"status": "on",
|
||||
"chain_count": 60,
|
||||
"preset": 8,
|
||||
"brightness": -1,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Get strip status
|
||||
HTTP request:
|
||||
```http
|
||||
GET /machine/wled/status?strip1&strip2
|
||||
```
|
||||
JSON-RPC request:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method":"machine.wled.status",
|
||||
"params": {
|
||||
"lights": null,
|
||||
"desk": null
|
||||
},
|
||||
"id": 7124
|
||||
}
|
||||
```
|
||||
Returns:
|
||||
|
||||
Strip information for requested strips.
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"lights": {
|
||||
"strip": "lights",
|
||||
"status": "on",
|
||||
"chain_count": 79,
|
||||
"preset": -1,
|
||||
"brightness": 255,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
},
|
||||
"desk": {
|
||||
"strip": "desk",
|
||||
"status": "on",
|
||||
"chain_count": 60,
|
||||
"preset": 8,
|
||||
"brightness": -1,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Turn strip on
|
||||
Turns the specified strips on to the initial colors or intial preset.
|
||||
|
||||
HTTP request:
|
||||
```http
|
||||
POST /machine/wled/on?strip1&strip2
|
||||
```
|
||||
JSON-RPC request:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method":"machine.wled.on",
|
||||
"params": {
|
||||
"lights": null,
|
||||
"desk": null
|
||||
},
|
||||
"id": 7125
|
||||
}
|
||||
```
|
||||
Returns:
|
||||
|
||||
Strip information for requested strips.
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"lights": {
|
||||
"strip": "lights",
|
||||
"status": "on",
|
||||
"chain_count": 79,
|
||||
"preset": -1,
|
||||
"brightness": 255,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
},
|
||||
"desk": {
|
||||
"strip": "desk",
|
||||
"status": "on",
|
||||
"chain_count": 60,
|
||||
"preset": 8,
|
||||
"brightness": -1,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Turn strip off
|
||||
Turns off all specified strips.
|
||||
|
||||
HTTP request:
|
||||
```http
|
||||
POST /machine/wled/off?strip1&strip2
|
||||
```
|
||||
JSON-RPC request:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method":"machine.wled.off",
|
||||
"params": {
|
||||
"lights": null,
|
||||
"desk": null
|
||||
},
|
||||
"id": 7126
|
||||
}
|
||||
```
|
||||
Returns:
|
||||
|
||||
The new state of the specified strips.
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"lights": {
|
||||
"strip": "lights",
|
||||
"status": "off",
|
||||
"chain_count": 79,
|
||||
"preset": -1,
|
||||
"brightness": 255,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
},
|
||||
"desk": {
|
||||
"strip": "desk",
|
||||
"status": "off",
|
||||
"chain_count": 60,
|
||||
"preset": 8,
|
||||
"brightness": -1,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Toggle strip on/off state
|
||||
Turns each strip off if it is on and on if it is off.
|
||||
|
||||
HTTP request:
|
||||
```http
|
||||
POST /machine/wled/off?strip1&strip2
|
||||
```
|
||||
JSON-RPC request:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method":"machine.wled.toggle",
|
||||
"params": {
|
||||
"lights": null,
|
||||
"desk": null
|
||||
},
|
||||
"id": 7127
|
||||
}
|
||||
```
|
||||
Returns:
|
||||
|
||||
The new state of the specified strips.
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"lights": {
|
||||
"strip": "lights",
|
||||
"status": "on",
|
||||
"chain_count": 79,
|
||||
"preset": -1,
|
||||
"brightness": 255,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
},
|
||||
"desk": {
|
||||
"strip": "desk",
|
||||
"status": "off",
|
||||
"chain_count": 60,
|
||||
"preset": 8,
|
||||
"brightness": -1,
|
||||
"intensity": -1,
|
||||
"speed": -1,
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Control individual strip state
|
||||
Toggle, turn on, turn off, turn on with preset, turn on with brightness, or
|
||||
turn on preset will some of brightness, intensity, and speed. Or simply set
|
||||
some of brightness, intensity, and speed.
|
||||
|
||||
HTTP requests:
|
||||
|
||||
Turn strip `lights` off
|
||||
```http
|
||||
POST /machine/wled/strip?strip=lights&action=off
|
||||
```
|
||||
|
||||
Turn strip `lights` on to the initial colors or intial preset.
|
||||
```http
|
||||
POST /machine/wled/strip?strip=lights&action=on
|
||||
```
|
||||
|
||||
Turn strip `lights` on activating preset 3.
|
||||
```http
|
||||
POST /machine/wled/strip?strip=lights&action=on&preset=3
|
||||
```
|
||||
|
||||
Turn strip `lights` on activating preset 3 while specifying speed and
|
||||
intensity.
|
||||
```http
|
||||
POST /machine/wled/strip?strip=lights&action=on&preset=3&intensity=50&speed=255
|
||||
```
|
||||
|
||||
Change strip `lights` brightness (if on) and speed (if a preset is active).
|
||||
```http
|
||||
POST /machine/wled/strip?strip=lights&action=control&brightness=99&speed=50
|
||||
```
|
||||
|
||||
JSON-RPC request:
|
||||
|
||||
Returns information for the specified strip.
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method":"machine.wled.get_strip",
|
||||
"params": {
|
||||
"strip": "lights",
|
||||
},
|
||||
"id": 7128
|
||||
}
|
||||
```
|
||||
|
||||
Calls the action with the arguments for the specified strip.
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method":"machine.wled.post_strip",
|
||||
"params": {
|
||||
"strip": "lights",
|
||||
"action": "on",
|
||||
"preset": 1,
|
||||
"brightness": 255,
|
||||
"intensity": 255,
|
||||
"speed": 255
|
||||
},
|
||||
"id": 7129
|
||||
}
|
||||
```
|
||||
!!! note
|
||||
The `action` argument may be `on`, `off`, `toggle` or `control`. Any
|
||||
other value will result in an error.
|
||||
|
||||
The `intensity` and `speed` arguments are only used if a preset is active.
|
||||
Permitted ranges are 1-255 for `brightness` and 0-255 for `intensity` and
|
||||
`speed`. When action is `on` a `preset` with some or all of `brightness`,
|
||||
`intensity` and `speed` may also be specified. If the action `control` is used
|
||||
one or all of `brightness`, `intensity`, and `speed` must be specified.
|
||||
|
||||
Returns:
|
||||
|
||||
State of the strip.
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"lights": {
|
||||
"strip": "lights",
|
||||
"status": "on",
|
||||
"chain_count": 79,
|
||||
"preset": 1,
|
||||
"brightness": 50,
|
||||
"intensity": 255,
|
||||
"speed": 255,
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### OctoPrint API emulation
|
||||
Partial support of OctoPrint API is implemented with the purpose of
|
||||
|
|
|
@ -65,9 +65,12 @@ class Strip():
|
|||
def get_strip_info(self: Strip) -> Dict[str, Any]:
|
||||
return {
|
||||
"strip": self.name,
|
||||
"status": self.onoff,
|
||||
"status": self.onoff.value,
|
||||
"chain_count": self.chain_count,
|
||||
"preset": self.preset,
|
||||
"brightness": self.brightness,
|
||||
"intensity": self.intensity,
|
||||
"speed": self.speed,
|
||||
"error": self.error_state
|
||||
}
|
||||
|
||||
|
@ -75,6 +78,9 @@ class Strip():
|
|||
self.send_full_chain_data = True
|
||||
self.onoff = OnOff.on
|
||||
self.preset = self.initial_preset
|
||||
self.brightness = 255
|
||||
self.intensity = -1
|
||||
self.speed = -1
|
||||
if self.initial_preset >= 0:
|
||||
self._update_color_data(self.initial_red,
|
||||
self.initial_green,
|
||||
|
@ -133,6 +139,10 @@ class Strip():
|
|||
else:
|
||||
self.send_full_chain_data = True
|
||||
self.preset = preset
|
||||
# Without reading the data back from wled we don't know the values
|
||||
self.brightness = -1
|
||||
self.intensity = -1
|
||||
self.speed = -1
|
||||
await self._send_wled_command({"on": True, "ps": preset})
|
||||
|
||||
async def wled_off(self: Strip) -> None:
|
||||
|
@ -143,6 +153,60 @@ class Strip():
|
|||
self.send_full_chain_data = True
|
||||
await self._send_wled_command({"on": False})
|
||||
|
||||
async def wled_control(self: Strip, brightness: int, intensity: int,
|
||||
speed: int) -> None:
|
||||
logging.debug(
|
||||
f"WLED: {self.name} control {self.onoff} BRIGHTNESS={brightness} "
|
||||
f"INTENSITY={intensity} SPEED={speed} CURRENTPRESET={self.preset}")
|
||||
|
||||
if self.onoff == OnOff.off:
|
||||
logging.info("wled control only permitted when strip is on")
|
||||
return
|
||||
|
||||
# Even if a preset is not activated sending seg {} information will
|
||||
# turn it back on
|
||||
control: Dict[str, Any]
|
||||
if self.preset != -1:
|
||||
control = {"tt": 0, "seg": {}}
|
||||
else:
|
||||
control = {"tt": 0}
|
||||
|
||||
shouldSend: bool = False
|
||||
# Using 0 is not recommended in wled docs
|
||||
if brightness > 0:
|
||||
if brightness > 255:
|
||||
logging.info("BRIGHTNESS should be between 1 and 255")
|
||||
else:
|
||||
shouldSend = True
|
||||
self.brightness = brightness
|
||||
control["bri"] = self.brightness
|
||||
# Brightness in seg {} - only if a preset is on
|
||||
if self.preset != -1:
|
||||
control["seg"]["bri"] = self.brightness
|
||||
|
||||
# Intensity - only if a preset is on
|
||||
if intensity > -1 and self.preset != -1:
|
||||
if intensity > 255:
|
||||
logging.info("INTENSITY should be between 0 and 255")
|
||||
else:
|
||||
shouldSend = True
|
||||
self.intensity = intensity
|
||||
control["seg"]["ix"] = self.intensity
|
||||
|
||||
# Speed - only if a preset is on
|
||||
if speed > -1 and self.preset != -1:
|
||||
if speed > 255:
|
||||
logging.info("SPEED should be between 0 and 255")
|
||||
else:
|
||||
shouldSend = True
|
||||
self.speed = speed
|
||||
control["seg"]["sx"] = self.speed
|
||||
|
||||
# Control brightness, intensity, and speed for segment
|
||||
# This will allow full control for effects such as "Percent"
|
||||
if shouldSend:
|
||||
await self._send_wled_command(control)
|
||||
|
||||
def _wled_pixel(self: Strip, index: int) -> List[int]:
|
||||
led_color_data: List[int] = []
|
||||
for p in self._chain_data[(index-1)*self._COLORSIZE:
|
||||
|
@ -158,13 +222,21 @@ class Strip():
|
|||
f"INDEX={index} TRANSMIT={transmit}")
|
||||
self._update_color_data(red, green, blue, white, index)
|
||||
if transmit:
|
||||
# Clear preset (issues with sending seg{} will revert to preset)
|
||||
self.preset = -1
|
||||
|
||||
# If we are coming from a preset without a wled_control
|
||||
# we don't know a brightness, this will also ensure
|
||||
# behaviour is consistent prior to introduction of wled_control
|
||||
if self.brightness == -1:
|
||||
self.brightness = 255
|
||||
|
||||
# Base command for setting an led (for all active segments)
|
||||
# See https://kno.wled.ge/interfaces/json-api/
|
||||
state: Dict[str, Any] = {"on": True,
|
||||
"tt": 0,
|
||||
"bri": 255,
|
||||
"seg": {"bri": 255, "i": []}}
|
||||
"bri": self.brightness,
|
||||
"seg": {"bri": self.brightness, "i": []}}
|
||||
if index is None:
|
||||
# All pixels same color only send range command of first color
|
||||
self.send_full_chain_data = False
|
||||
|
@ -329,6 +401,9 @@ class WLED:
|
|||
self.server.register_endpoint(
|
||||
"/machine/wled/off", ["POST"],
|
||||
self._handle_batch_wled_request)
|
||||
self.server.register_endpoint(
|
||||
"/machine/wled/toggle", ["POST"],
|
||||
self._handle_batch_wled_request)
|
||||
self.server.register_endpoint(
|
||||
"/machine/wled/strip", ["GET", "POST"],
|
||||
self._handle_single_wled_request)
|
||||
|
@ -372,8 +447,9 @@ class WLED:
|
|||
# Full control of wled
|
||||
# state: True, False, "on", "off"
|
||||
# preset: wled preset (int) to use (ignored if state False or "Off")
|
||||
async def set_wled_state(self: WLED, strip: str, state: str,
|
||||
preset: int = -1) -> None:
|
||||
async def set_wled_state(self: WLED, strip: str, state: str = None,
|
||||
preset: int = -1, brightness: int = -1,
|
||||
intensity: int = -1, speed: int = -1) -> None:
|
||||
status = None
|
||||
|
||||
if isinstance(state, bool):
|
||||
|
@ -383,21 +459,28 @@ class WLED:
|
|||
if status in ["true", "false"]:
|
||||
status = OnOff.on if status == "true" else OnOff.off
|
||||
|
||||
if status is None and preset == -1:
|
||||
if status is None and preset == -1 and brightness == -1 and \
|
||||
intensity == -1 and speed == -1:
|
||||
logging.info(
|
||||
f"Invalid state received but no preset passed: {state}")
|
||||
f"Invalid state received but no control or preset data passed")
|
||||
return
|
||||
|
||||
if strip not in self.strips:
|
||||
logging.info(f"Unknown WLED strip: {strip}")
|
||||
return
|
||||
|
||||
if status == OnOff.off:
|
||||
# All other arguments are ignored
|
||||
if status == OnOff.off:
|
||||
await self.strips[strip].wled_off()
|
||||
else:
|
||||
|
||||
# Turn on if on or a preset is specified
|
||||
if status == OnOff.on or preset != -1:
|
||||
await self.strips[strip].wled_on(preset)
|
||||
|
||||
# Control
|
||||
if brightness != -1 or intensity != -1 or speed != -1:
|
||||
await self.strips[strip].wled_control(brightness, intensity, speed)
|
||||
|
||||
# Individual pixel control, for compatibility with SET_LED
|
||||
async def set_wled(self: WLED,
|
||||
strip: str,
|
||||
|
@ -429,6 +512,9 @@ class WLED:
|
|||
) -> Dict[str, Any]:
|
||||
strip_name: str = web_request.get_str('strip')
|
||||
preset: int = web_request.get_int('preset', -1)
|
||||
brightness: int = web_request.get_int('brightness', -1)
|
||||
intensity: int = web_request.get_int('intensity', -1)
|
||||
speed: int = web_request.get_int('speed', -1)
|
||||
|
||||
req_action = web_request.get_action()
|
||||
if strip_name not in self.strips:
|
||||
|
@ -438,10 +524,11 @@ class WLED:
|
|||
return {strip_name: strip.get_strip_info()}
|
||||
elif req_action == "POST":
|
||||
action = web_request.get_str('action').lower()
|
||||
if action not in ["on", "off", "toggle"]:
|
||||
if action not in ["on", "off", "toggle", "control"]:
|
||||
raise self.server.error(
|
||||
f"Invalid requested action '{action}'")
|
||||
result = await self._process_request(strip, action, preset)
|
||||
result = await self._process_request(strip, action, preset,
|
||||
brightness, intensity, speed)
|
||||
return {strip_name: result}
|
||||
|
||||
async def _handle_batch_wled_request(self: WLED,
|
||||
|
@ -456,7 +543,8 @@ class WLED:
|
|||
req = ep.split("/")[-1]
|
||||
for name, strip in requested_strips.items():
|
||||
if strip is not None:
|
||||
result[name] = await self._process_request(strip, req, -1)
|
||||
result[name] = await self._process_request(strip, req, -1,
|
||||
-1, -1, -1)
|
||||
else:
|
||||
result[name] = {"error": "strip_not_found"}
|
||||
return result
|
||||
|
@ -464,7 +552,10 @@ class WLED:
|
|||
async def _process_request(self: WLED,
|
||||
strip: Strip,
|
||||
req: str,
|
||||
preset: int
|
||||
preset: int,
|
||||
brightness: int,
|
||||
intensity: int,
|
||||
speed: int
|
||||
) -> Dict[str, Any]:
|
||||
strip_onoff = strip.onoff
|
||||
|
||||
|
@ -472,15 +563,21 @@ class WLED:
|
|||
return strip.get_strip_info()
|
||||
if req == "toggle":
|
||||
req = "on" if strip_onoff == OnOff.off else "off"
|
||||
if req in ["on", "off"]:
|
||||
|
||||
if req in ["on", "off", "control"]:
|
||||
# Always do something, could be turning off colors, or changing
|
||||
# preset, easier not to have to worry
|
||||
if req == "on" or req == "control":
|
||||
if req == "on":
|
||||
strip_onoff = OnOff.on
|
||||
await strip.wled_on(preset)
|
||||
|
||||
if brightness != -1 or intensity != -1 or speed != -1:
|
||||
await strip.wled_control(brightness, intensity, speed)
|
||||
else:
|
||||
strip_onoff = OnOff.off
|
||||
await strip.wled_off()
|
||||
|
||||
return strip.get_strip_info()
|
||||
|
||||
raise self.server.error(f"Unsupported wled request: {req}")
|
||||
|
|
Loading…
Reference in New Issue