@@ -144,60 +144,78 @@ async def set_device_led(self, device: Device, enable: bool = True) -> bool:
144144 like brightness and color control, a Light entity would be more appropriate.
145145
146146 Implementation:
147- Sends only essential device fields to avoid InvalidPayload errors on older
148- UniFi Network versions that reject read-only fields in update requests.
147+ Uses DeviceSetLedStatus with fallback to minimal payload of device endpoint.
149148 """
150149 try :
151150 device_id = device .id
152- status = "on" if enable else "off"
153-
154- # Get device MAC safely for logging
155151 device_raw = getattr (device , 'raw' , {}) if hasattr (device , 'raw' ) else {}
156152 device_mac = device_raw .get ('mac' , device_raw .get ('serial' , 'unknown' ))
157153
158- # Create minimal payload with only essential fields to avoid InvalidPayload errors
159- # Many fields in the full device payload are read-only and cause API errors
160- device_payload = {
161- '_id' : device_id ,
162- 'led_override' : status
163- }
154+ LOGGER .debug ("Attempting LED toggle for device %s (MAC: %s) to %s" ,
155+ device_id , device_mac , "on" if enable else "off" )
164156
165- # Include optional LED fields if they exist in the original data
166- # to maintain any existing LED configuration
167- if 'led_override_color' in device_raw :
168- device_payload ['led_override_color' ] = device_raw ['led_override_color' ]
169- if 'led_override_color_brightness' in device_raw :
170- device_payload ['led_override_color_brightness' ] = device_raw ['led_override_color_brightness' ]
157+ # Approach 1: Try using DeviceSetLedStatus from aiounifi first
158+ try :
159+ LOGGER .debug ("Trying DeviceSetLedStatus approach for %s" , device_mac )
160+
161+ # Use 'default' for enabled (normal LED behavior), 'off' for disabled
162+ led_value = "default" if enable else "off"
163+
164+ request = DeviceSetLedStatus .create (device , led_value )
165+ result = await self .controller .request (request )
166+
167+ if result and isinstance (result , dict ):
168+ meta = result .get ('meta' , {})
169+ if meta .get ('rc' ) == 'ok' :
170+ LOGGER .debug ("DeviceSetLedStatus succeeded for %s with value: %s" , device_mac , led_value )
171+ # Update local state
172+ if hasattr (device , 'raw' ) and device .raw :
173+ device .raw ['led_override' ] = led_value
174+ return True
175+ else :
176+ LOGGER .debug ("DeviceSetLedStatus failed for %s: %s" , device_mac , meta )
177+
178+ except Exception as err :
179+ LOGGER .debug ("DeviceSetLedStatus approach failed for %s: %s" , device_mac , err )
171180
172- # Use the legacy API endpoint for device updates (not v2)
173- # Path format: /rest/device/{device_id}
174- path = f"/rest/device/{ device_id } "
175- request = self .create_api_request ("PUT" , path , data = device_payload , is_v2 = False )
181+ # Approach 2: Fallback to minimal payload of device endpoint
182+ try :
183+ LOGGER .debug ("Trying minimal payload approach (original working method) for %s" , device_mac )
184+
185+ # Use original working LED values: 'on' for enabled, 'off' for disabled
186+ led_value = "on" if enable else "off"
187+ device_payload = {
188+ '_id' : device_id ,
189+ 'led_override' : led_value
190+ }
191+
192+ path = f"/rest/device/{ device_id } "
193+ request = self .create_api_request ("PUT" , path , data = device_payload , is_v2 = False )
194+ result = await self .controller .request (request )
195+
196+ if result and isinstance (result , dict ):
197+ meta = result .get ('meta' , {})
198+ if meta .get ('rc' ) == 'ok' :
199+ LOGGER .debug ("Minimal payload approach succeeded for %s with value: %s" , device_mac , led_value )
200+ # Update local state
201+ if hasattr (device , 'raw' ) and device .raw :
202+ device .raw ['led_override' ] = led_value
203+ return True
204+ else :
205+ LOGGER .debug ("Minimal payload approach failed for %s: %s" , device_mac , meta )
206+
207+ except Exception as err :
208+ LOGGER .debug ("Minimal payload approach failed for %s: %s" , device_mac , err )
176209
177- result = await self .controller .request (request )
210+ # Both approaches failed
211+ LOGGER .error ("Both LED toggle approaches failed for device %s (MAC: %s)" , device_id , device_mac )
212+ return False
178213
179- # Check for successful response
180- if result and isinstance (result , dict ):
181- meta = result .get ('meta' , {})
182- if meta .get ('rc' ) == 'ok' :
183- LOGGER .debug ("Device %s LED set to %s" , device_mac , status )
184- # Update the device's local state for immediate feedback
185- if hasattr (device , 'raw' ) and device .raw :
186- device .raw ['led_override' ] = status
187- elif hasattr (device , 'led_override' ):
188- device .led_override = status
189- return True
190- else :
191- LOGGER .error ("API returned error for device %s LED update: %s" , device_mac , meta )
192- return False
193- else :
194- LOGGER .error ("Failed to set LED for device %s - unexpected API response" , device_mac )
195- return False
196214 except Exception as err :
197215 # Get device MAC safely for error logging
198216 device_raw = getattr (device , 'raw' , {}) if hasattr (device , 'raw' ) else {}
199217 device_mac = device_raw .get ('mac' , device_raw .get ('serial' , 'unknown' ))
200- LOGGER .error ("Error setting device LED for %s: %s" , device_mac , str (err ))
218+ LOGGER .error ("Critical error in LED toggle for %s: %s" , device_mac , str (err ))
201219 return False
202220
203221 async def get_device_led_states (self ) -> Dict [str , Dict [str , Any ]]:
0 commit comments