Coverage for farmbot/main.py: 100%

145 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-09-12 12:18 -0700

1""" 

2Farmbot class. 

3""" 

4 

5from .state import State 

6from .functions.api import ApiConnect 

7from .functions.basic_commands import BasicCommands 

8from .functions.broker import BrokerConnect 

9from .functions.camera import Camera 

10from .functions.information import Information 

11from .functions.jobs import JobHandling 

12from .functions.messages import MessageHandling 

13from .functions.movements import MovementControls 

14from .functions.peripherals import Peripherals 

15from .functions.resources import Resources 

16from .functions.tools import ToolControls 

17 

18VERSION = "2.0.0" 

19 

20 

21class Farmbot(): 

22 """Farmbot class.""" 

23 __version__ = VERSION 

24 

25 def __init__(self): 

26 self.state = State() 

27 

28 # Initialize other components without the token initially 

29 self.api = ApiConnect(self.state) 

30 self.basic = BasicCommands(self.state) 

31 self.broker = BrokerConnect(self.state) 

32 self.camera = Camera(self.state) 

33 self.info = Information(self.state) 

34 self.jobs = JobHandling(self.state) 

35 self.messages = MessageHandling(self.state) 

36 self.movements = MovementControls(self.state) 

37 self.peripherals = Peripherals(self.state) 

38 self.resources = Resources(self.state) 

39 self.tools = ToolControls(self.state) 

40 

41 def set_verbosity(self, value): 

42 """Set output verbosity level.""" 

43 self.state.verbosity = value 

44 

45 def set_timeout(self, duration, key="listen"): 

46 """Set timeout value in seconds.""" 

47 if key == "all": 

48 for timeout_key in self.state.timeout: 

49 self.state.timeout[timeout_key] = duration 

50 else: 

51 self.state.timeout[key] = duration 

52 

53 def set_token(self, token): 

54 """Set FarmBot authorization token.""" 

55 self.state.token = token 

56 

57 def clear_cache(self, endpoint=None): 

58 """Clear cached records.""" 

59 self.state.clear_cache(endpoint) 

60 

61 # api.py 

62 

63 def get_token(self, email, password, server="https://my.farm.bot"): 

64 """Get FarmBot authorization token. Server is 'https://my.farm.bot' by default.""" 

65 return self.api.get_token(email, password, server) 

66 

67 # basic_commands.py 

68 

69 def wait(self, duration): 

70 """Pauses execution for a certain number of milliseconds.""" 

71 return self.basic.wait(duration) 

72 

73 def e_stop(self): 

74 """Emergency locks (E-stops) the Farmduino microcontroller.""" 

75 return self.basic.e_stop() 

76 

77 def unlock(self): 

78 """Unlocks a locked (E-stopped) device.""" 

79 return self.basic.unlock() 

80 

81 def reboot(self): 

82 """Reboots the FarmBot OS and re-initializes the device.""" 

83 return self.basic.reboot() 

84 

85 def shutdown(self): 

86 """Shuts down the FarmBot OS and turns the device off.""" 

87 return self.basic.shutdown() 

88 

89 # broker.py 

90 

91 def connect_broker(self): 

92 """Establish persistent connection to send messages via message broker.""" 

93 return self.broker.connect() 

94 

95 def disconnect_broker(self): 

96 """Disconnect from the message broker.""" 

97 return self.broker.disconnect() 

98 

99 def publish(self, message): 

100 """Publish message to the message broker.""" 

101 return self.broker.publish(message) 

102 

103 def listen(self, channel="#", duration=None, stop_count=1): 

104 """Listen to a message broker channel.""" 

105 return self.broker.listen(channel, duration, stop_count=stop_count) 

106 

107 def listen_for_status_changes(self, 

108 duration=None, 

109 stop_count=1, 

110 diff_only=True, 

111 info_path=None): 

112 """Listen for status changes.""" 

113 return self.broker.listen( 

114 channel="status", 

115 duration=duration, 

116 stop_count=stop_count, 

117 message_options={"diff_only": diff_only, "path": info_path}, 

118 ) 

119 

120 # camera.py 

121 

122 def calibrate_camera(self): 

123 """Performs camera calibration. This action will reset camera calibration settings.""" 

124 return self.camera.calibrate_camera() 

125 

126 def take_photo(self): 

127 """Takes photo using the device camera and uploads it to the web app.""" 

128 return self.camera.take_photo() 

129 

130 # information.py 

131 

132 def api_get(self, endpoint, database_id=None): 

133 """Get information about a specific endpoint.""" 

134 return self.info.api_get(endpoint, database_id) 

135 

136 def api_patch(self, endpoint, new_data, database_id=None): 

137 """Change information contained within an endpoint.""" 

138 return self.info.api_patch(endpoint, new_data, database_id) 

139 

140 def api_post(self, endpoint, new_data): 

141 """Create new information contained within an endpoint.""" 

142 return self.info.api_post(endpoint, new_data) 

143 

144 def api_delete(self, endpoint, database_id=None): 

145 """Delete information contained within an endpoint.""" 

146 return self.info.api_delete(endpoint, database_id) 

147 

148 def safe_z(self): 

149 """Returns the highest safe point along the z-axis.""" 

150 return self.info.safe_z() 

151 

152 def garden_size(self): 

153 """Returns size of garden bed.""" 

154 return self.info.garden_size() 

155 

156 def get_curve(self, curve_id=None): 

157 """Returns the curve data.""" 

158 return self.info.get_curve(curve_id) 

159 

160 def measure_soil_height(self): 

161 """Use the camera to determine soil height at the current location.""" 

162 return self.info.measure_soil_height() 

163 

164 def read_status(self, path=None): 

165 """Returns the FarmBot status tree.""" 

166 return self.info.read_status(path) 

167 

168 def read_pin(self, pin_number, mode="digital"): 

169 """Reads the current value of the specified pin.""" 

170 return self.info.read_pin(pin_number, mode) 

171 

172 def read_sensor(self, sensor_name): 

173 """Reads the given sensor.""" 

174 return self.info.read_sensor(sensor_name) 

175 

176 # jobs.py 

177 

178 def get_job(self, job_name=None): 

179 """Retrieves the status or details of the specified job.""" 

180 return self.jobs.get_job(job_name) 

181 

182 def set_job(self, job_name, status, percent): 

183 """Initiates or modifies job with given parameters.""" 

184 return self.jobs.set_job(job_name, status, percent) 

185 

186 def complete_job(self, job_name): 

187 """Marks job as completed and triggers any associated actions.""" 

188 return self.jobs.complete_job(job_name) 

189 

190 # messages.py 

191 

192 def log(self, message_str, message_type="info", channels=None): 

193 """Sends new log message via the API.""" 

194 return self.messages.log(message_str, message_type, channels) 

195 

196 def send_message(self, message_str, message_type="info", channels=None): 

197 """Sends new log message via the message broker.""" 

198 return self.messages.send_message(message_str, message_type, channels) 

199 

200 def debug(self, message_str): 

201 """Sends debug message used for developer information or troubleshooting.""" 

202 return self.messages.debug(message_str) 

203 

204 def toast(self, message_str, message_type="info"): 

205 """Sends a message that pops up on the user interface briefly.""" 

206 return self.messages.toast(message_str, message_type) 

207 

208 # movements.py 

209 

210 def move(self, x=None, y=None, z=None, safe_z=None, speed=None): 

211 """Moves to the specified (x, y, z) coordinate.""" 

212 return self.movements.move(x, y, z, safe_z, speed) 

213 

214 def set_home(self, axis="all"): 

215 """Sets the current position as the home position for a specific axis.""" 

216 return self.movements.set_home(axis) 

217 

218 def find_home(self, axis="all", speed=100): 

219 """Moves the device to the home position for a specified axis.""" 

220 return self.movements.find_home(axis, speed) 

221 

222 def find_axis_length(self, axis="all"): 

223 """Finds the length of a specified axis.""" 

224 return self.movements.find_axis_length(axis) 

225 

226 def get_xyz(self): 

227 """Returns the current (x, y, z) coordinates of the FarmBot.""" 

228 return self.movements.get_xyz() 

229 

230 def check_position(self, coordinate, tolerance): 

231 """Verifies position of the FarmBot within specified tolerance range.""" 

232 return self.movements.check_position(coordinate, tolerance) 

233 

234 # peripherals.py 

235 

236 def control_servo(self, pin, angle): 

237 """Set servo angle between 0-180 degrees.""" 

238 return self.peripherals.control_servo(pin, angle) 

239 

240 def write_pin(self, pin_number, value, mode="digital"): 

241 """Writes a new value to the specified pin.""" 

242 return self.peripherals.write_pin(pin_number, value, mode) 

243 

244 def control_peripheral(self, peripheral_name, value, mode=None): 

245 """Set peripheral value and mode.""" 

246 return self.peripherals.control_peripheral(peripheral_name, value, mode) 

247 

248 def toggle_peripheral(self, peripheral_name): 

249 """Toggles the state of a specific peripheral between `on` and `off`.""" 

250 return self.peripherals.toggle_peripheral(peripheral_name) 

251 

252 def on(self, pin_number): 

253 """Turns specified pin number `on` (100%).""" 

254 return self.peripherals.on(pin_number) 

255 

256 def off(self, pin_number): 

257 """Turns specified pin number `off` (0%).""" 

258 return self.peripherals.off(pin_number) 

259 

260 # resources.py 

261 

262 def sequence(self, sequence_name): 

263 """Executes a predefined sequence.""" 

264 return self.resources.sequence(sequence_name) 

265 

266 def get_seed_tray_cell(self, tray_name, tray_cell): 

267 """Identifies and returns the location of specified cell in the seed tray.""" 

268 return self.resources.get_seed_tray_cell(tray_name, tray_cell) 

269 

270 def detect_weeds(self): 

271 """Scans the garden to detect weeds.""" 

272 return self.resources.detect_weeds() 

273 

274 def lua(self, lua_code): 

275 """Executes custom Lua code snippets to perform complex tasks or automations.""" 

276 return self.resources.lua(lua_code) 

277 

278 def if_statement(self, 

279 variable, 

280 operator, 

281 value, 

282 then_sequence_name=None, 

283 else_sequence_name=None, 

284 named_pin_type=None): 

285 """Performs conditional check and executes actions based on the outcome.""" 

286 return self.resources.if_statement( 

287 variable, 

288 operator, 

289 value, 

290 then_sequence_name, 

291 else_sequence_name, 

292 named_pin_type) 

293 

294 def assertion(self, lua_code, assertion_type, recovery_sequence_name=None): 

295 """Evaluates an expression.""" 

296 return self.resources.assertion(lua_code, assertion_type, recovery_sequence_name) 

297 

298 # tools.py 

299 

300 def mount_tool(self, tool_name): 

301 """Mounts the given tool and pulls it out of assigned slot.""" 

302 return self.tools.mount_tool(tool_name) 

303 

304 def dismount_tool(self): 

305 """Dismounts the currently mounted tool into assigned slot.""" 

306 return self.tools.dismount_tool() 

307 

308 def water(self, plant_id, tool_name=None, pin=None): 

309 """Moves to and waters plant based on age and assigned watering curve.""" 

310 return self.tools.water(plant_id, tool_name, pin) 

311 

312 def dispense(self, milliliters, tool_name=None, pin=None): 

313 """Dispenses user-defined amount of liquid in milliliters.""" 

314 return self.tools.dispense(milliliters, tool_name, pin)