3 from pathlib import Path
5 sys.path.insert(0, str(Path.cwd().parent))
6 from pyindi.device import device
8 from buildhat import Motor
9 from buildhat.motors import MotorRunmode
12 This file uses a skeleton xml file to initialize and
13 define properties. Similar to this example at indilib
14 https://www.indilib.org/developers/driver-howto.html#h2-properties
18 class LegoFocuser(device):
20 def ISGetProperties(self, device=None):
21 """Property Definiations are generated
22 by initProperties and buildSkeleton. No
23 need to do it here. """
26 def initProperties(self):
27 """Build the vector properties from
29 self.buildSkeleton("legofocuser.xml")
31 def ISNewText(self, device, name, names, values):
32 """A new text vector has been updated from
33 the client. In this case we update the text
34 vector with the IUUpdate function. In a real
35 device driver you would probably want to do
36 something more than that.
38 This function is always called by the
42 # BUILD_HAT_PORT - the port on the Build HAT to which the
45 if name == "BUILD_HAT_PORT":
47 port = self.IUUpdate(device, name, names, values)
49 port["PORT"].value = port["PORT"].value.strip()
51 except Exception as error:
52 self.IDMessage(f"BUILD_HAT_PORT error: {error}")
54 self.IUUpdate(device, name, names, values, Set=True)
56 def ISNewNumber(self, device, name, names, values):
58 """A number vector has been updated from the client.
61 # FOCUS_SPEED - the absolute speed at which the focuser travels
63 if name == "FOCUS_SPEED":
66 # update our idea of speed to what the client said
68 focus = self.IUUpdate(device, name, names, values)
70 self.update_speed_direction()
72 # No exceptions, so set the status to OK
77 except Exception as error:
78 self.IDMessage(f"FOCUS_SPEED error: {error}")
80 # Try to set the status to Alert (red)
87 # REL_FOCUS_POSITION - request a focuser move by
88 # FOCUS_RELATIVE_POSITION ticks
90 if name == "REL_FOCUS_POSITION":
93 # Update our idea of the request
95 focpos = self.IUUpdate(device, name, names, values)
96 focposval = focpos["FOCUS_RELATIVE_POSITION"].value
98 # Ticks are degrees for us. Speed is the motor's
101 self.motor.run_for_degrees(focposval, blocking=False)
103 # We now need to set the state to Busy until the motor
109 # And set a callback to return to OK when the motor
112 self.IEAddTimer(100, self.checkmotoridle)
114 except Exception as error:
115 self.IDMessage(f"IUUpdate error: {error}")
118 # ABS_FOCUS_POSITION - request a focuser move to
119 # FOCUS_ABSOLUTE_POSITION ticks
121 if name == "ABS_FOCUS_POSITION":
124 # Update our idea of the request
126 focpos = self.IUUpdate(device, name, names, values)
127 focposval = focpos["FOCUS_ABSOLUTE_POSITION"].value
129 # Assuming 0 is the bottom of the focuser travel
130 # we move to the new position
132 curpos = self.motor.get_position()
133 self.motor.run_for_degrees(focposval-curpos, blocking=False)
135 # We now need to set the state to Busy until the motor
141 # And set a callback to return to OK when the motor
144 self.IEAddTimer(100, self.checkmotoridle)
146 except Exception as error:
147 self.IDMessage(f"IUUpdate error: {error}")
150 if name == "FOCUS_TIMER":
153 focus = self.IUUpdate(device, name, names, values)
154 if focus["FOCUS_TIMER_VALUE"].value != 0:
155 motor.run_for_seconds(1)
156 # focus["FOCUS_TIMER_VALUE"].value / 1000)
157 except Exception as error:
158 self.IDMessage(f"IUUpdate error: {error}")
162 def ISNewSwitch(self, device, name, names, values):
164 """A numer switch has been updated from the client.
165 This function handles when a new switch
167 This function is always called by the
171 if name == "CONNECTION":
174 conn = self.IUUpdate(device, name, names, values)
175 if conn["CONNECT"].value == 'Off':
177 self._is_connected = False
181 prt = self.IUFind("BUILD_HAT_PORT")
182 portval = prt["PORT"].value.strip()
183 self.motor = Motor(portval)
185 self._is_connected = True
189 except Exception as error:
190 self.IDMessage(f"IUUpdate error: {error}")
193 if name == "FOCUS_MOTION":
195 focus = self.IUUpdate(device, name, names, values, Set=True)
197 self.update_speed_direction()
199 # No exceptions, so set the status to OK
204 except Exception as error:
205 self.IDMessage(f"FOCUS_MOTION error: {error}")
207 # Try to set the status to Alert (red)
212 if name == "FOCUS_ABORT_MOTION":
215 fam = self.IUFind("FOCUS_ABORT_MOTION")
216 fam["ABORT"].value = "Off"
219 except Exception as error:
220 self.IDMessage(f"FOCUS_ABORT_MOTION error: {error}")
225 def checkmotoridle(self):
226 if self.motor._runmode == MotorRunmode.NONE:
227 focpos = self.IUFind("REL_FOCUS_POSITION")
230 focpos = self.IUFind("ABS_FOCUS_POSITION")
234 self.IEAddTimer(100, self.checkmotoridle)
236 def run_until_resistance(self):
238 # run motor until it appears it encounters resistance
239 # returns number of degrees travelled
241 # Initial absolute position of motor
243 ipos = self.motor.get_aposition()
247 while abs(pos-ipos) < 15:
248 self.motor.run_for_degrees(360)
249 self.motor.run_to_position(ipos)
250 pos = self.motor.get_position()
255 def update_speed_direction(self):
259 speed = int(self.IUFind("FOCUS_SPEED")["FOCUS_SPEED_VALUE"].value)
261 # since the Build HAT API uses positive/negative speed
262 # for direction, pull out the direction of focus motion
264 direction = 1 if (self.IUFind("FOCUS_MOTION"))["FOCUS_INWARD"].value == 'On' else -1
266 # And flip if the INDI focus reverse switch is set
268 if (self.IUFind("FOCUS_REVERSE_MOTION"))["ENABLED"].value == 'On':
271 # And send the result to the HAT.
273 self.motor.set_default_speed(speed * direction)