@@ -117,6 +117,10 @@ def next_feeding_end_time(self) -> str:
117117 except ValueError :
118118 return "Invalid time"
119119
120+ def __init__ (self , * args , ** kwargs ):
121+ super ().__init__ (* args , ** kwargs )
122+ self ._rotating = False
123+
120124 @property
121125 def manual_feed_id (self ) -> int :
122126 """Returns the manual feed ID."""
@@ -187,50 +191,62 @@ async def set_manual_feed_now(self, start: bool, plate: int) -> None:
187191 await self .api .set_manual_feed_now (self .serial , plate )
188192 else :
189193 _LOGGER .debug (f"Triggering stop feed now for { self .serial } " )
190- await self .api .set_stop_feed_now (self .serial , self .manual_feed_id )
194+ if self .manual_feed_id is None :
195+ _LOGGER .warning ("No manual feed ID available to stop for %s" , self .serial )
196+ else :
197+ await self .api .set_stop_feed_now (self .serial , self .manual_feed_id )
191198
192- await self .refresh () # Refresh the state after the action
199+ await self .refresh ()
193200 except aiohttp .ClientError as err :
194201 _LOGGER .error (f"Failed to trigger manual feed now for { self .serial } with plate no.{ plate } : { err } " )
195202 raise PetLibroAPIError (f"Error triggering manual feed now: { err } " )
196203
197204 async def set_plate_position (self , value : str | int ) -> None :
198205 """Rotate bowl to requested plate (1-3)"""
206+ if self ._rotating :
207+ _LOGGER .warning ("Rotation already in progress for %s" , self .serial )
208+ return
199209 try :
200210 target = int (value )
201211 except (TypeError , ValueError ):
202212 raise PetLibroAPIError (f"Invalid plate value: { value !r} " )
203213 if target not in (1 , 2 , 3 ):
204- # Raise an error if plate count somehow became less than 1 or more than 3.
205214 raise PetLibroAPIError (f"Plate must be 1, 2, or 3, got { target } " )
206215
207- # Ensure we know current position
208216 if not self .plate_position :
209217 await self .refresh ()
210218 curr = self .plate_position or 1
211219
212220 steps = (target - curr ) % 3
213221 _LOGGER .debug ("Rotate-to-plate: curr=%s target=%s steps=%s for %s" , curr , target , steps , self .serial )
214222
215- # didnt test other cooldowns, may be able reduce.
216- ROTATE_COOLDOWN = 0.6
217- for _ in range (steps ):
218- await self .api .set_rotate_food_bowl (self .serial )
219- await asyncio .sleep (ROTATE_COOLDOWN )
220- await self .refresh ()
223+ self ._rotating = True
224+ try :
225+ ROTATE_COOLDOWN = 2.0
226+ for _ in range (steps ):
227+ await self .api .set_rotate_food_bowl (self .serial )
228+ await asyncio .sleep (ROTATE_COOLDOWN )
229+ await self .refresh ()
221230
222- # Final refresh so sensor/current_option show the target
223- await self .refresh ()
231+ await self .refresh ()
232+ finally :
233+ self ._rotating = False
224234
225235 async def rotate_food_bowl (self ) -> None :
236+ if self ._rotating :
237+ _LOGGER .warning ("Rotation already in progress for %s" , self .serial )
238+ return
226239 _LOGGER .debug (f"Triggering rotate food bowl for { self .serial } " )
227240
241+ self ._rotating = True
228242 try :
229243 await self .api .set_rotate_food_bowl (self .serial )
230- await self .refresh () # Refresh the state after the action
244+ await self .refresh ()
231245 except aiohttp .ClientError as err :
232246 _LOGGER .error (f"Failed to trigger rotate food bowl for { self .serial } : { err } " )
233247 raise PetLibroAPIError (f"Error triggering rotate food bowl: { err } " )
248+ finally :
249+ self ._rotating = False
234250
235251 async def feed_audio (self ) -> None :
236252 _LOGGER .debug (f"Triggering feed audio for { self .serial } " )
0 commit comments