Coverage for farmbot_sidecar_starter_pack/functions/information.py: 100%

116 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-09-11 15:43 -0700

1""" 

2Information class. 

3""" 

4 

5# └── functions/information 

6# ├── [API] api_get() 

7# ├── [API] api_patch() 

8# ├── [API] api_post() 

9# ├── [API] api_delete() 

10# ├── [API] safe_z() 

11# ├── [API] garden_size() 

12# ├── [API] group() 

13# ├── [API] curve() 

14# ├── [BROKER] measure_soil_height() 

15# ├── [BROKER] read_status() 

16# ├── [BROKER] read_pin() 

17# └── [BROKER] read_sensor() 

18 

19from .broker import BrokerConnect 

20from .api import ApiConnect 

21 

22 

23class Information(): 

24 """Information class.""" 

25 

26 def __init__(self, state): 

27 self.broker = BrokerConnect(state) 

28 self.api = ApiConnect(state) 

29 self.state = state 

30 

31 def api_get(self, endpoint, database_id=None, data_print=True): 

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

33 self.state.print_status( 

34 description=f"Retrieving {endpoint} information.") 

35 

36 endpoint_data = self.api.request("GET", endpoint, database_id) 

37 

38 if data_print: 

39 self.state.print_status( 

40 update_only=True, 

41 endpoint_json=endpoint_data) 

42 else: 

43 self.state.print_status( 

44 update_only=True, 

45 description=f"Fetched {len(endpoint_data)} items.") 

46 

47 return endpoint_data 

48 

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

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

51 self.state.print_status(description=f"Editing {endpoint}.") 

52 

53 result = self.api.request( 

54 method="PATCH", 

55 endpoint=endpoint, 

56 database_id=database_id, 

57 payload=new_data) 

58 

59 self.state.print_status(update_only=True, endpoint_json=result) 

60 

61 return result 

62 

63 def api_post(self, endpoint, new_data): 

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

65 self.state.print_status(description=f"Adding new data to {endpoint}.") 

66 

67 result = self.api.request( 

68 method="POST", 

69 endpoint=endpoint, 

70 database_id=None, 

71 payload=new_data) 

72 

73 self.state.print_status(update_only=True, endpoint_json=result) 

74 

75 return result 

76 

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

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

79 self.state.print_status( 

80 description=f"Deleting {endpoint} with id={database_id}.") 

81 

82 result = self.api.request("DELETE", endpoint, database_id=database_id) 

83 

84 self.state.print_status(update_only=True, endpoint_json=result) 

85 

86 return result 

87 

88 def safe_z(self): 

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

90 self.state.print_status(description="Retrieving safe z value...") 

91 

92 config_data = self.api_get('fbos_config') 

93 z_value = config_data["safe_height"] 

94 

95 self.state.print_status( 

96 description=f"Safe z={z_value}", update_only=True) 

97 return z_value 

98 

99 def garden_size(self): 

100 """Return size of garden bed.""" 

101 self.state.print_status(description="Retrieving garden size...") 

102 

103 json_data = self.api_get('firmware_config') 

104 

105 x_steps = json_data['movement_axis_nr_steps_x'] 

106 x_mm = json_data['movement_step_per_mm_x'] 

107 

108 y_steps = json_data['movement_axis_nr_steps_y'] 

109 y_mm = json_data['movement_step_per_mm_y'] 

110 

111 z_steps = json_data['movement_axis_nr_steps_z'] 

112 z_mm = json_data['movement_step_per_mm_z'] 

113 

114 garden_size = { 

115 "x": x_steps / x_mm, 

116 "y": y_steps / y_mm, 

117 "z": z_steps / z_mm, 

118 } 

119 

120 self.state.print_status(endpoint_json=garden_size, update_only=True) 

121 return garden_size 

122 

123 def group(self, group_id=None): 

124 """Returns all group info or single by id.""" 

125 self.state.print_status(description="Retrieving group information...") 

126 

127 if group_id is None: 

128 group_data = self.api_get("point_groups") 

129 else: 

130 group_data = self.api_get('point_groups', group_id) 

131 

132 self.state.print_status(endpoint_json=group_data, update_only=True) 

133 return group_data 

134 

135 def curve(self, curve_id=None): 

136 """Returns all curve info or single by id.""" 

137 self.state.print_status(description="Retrieving curve information...") 

138 

139 if curve_id is None: 

140 curve_data = self.api_get("curves") 

141 else: 

142 curve_data = self.api_get('curves', curve_id) 

143 

144 self.state.print_status(endpoint_json=curve_data, update_only=True) 

145 return curve_data 

146 

147 def measure_soil_height(self): 

148 """Use the camera to measure the soil height at the current location.""" 

149 self.state.print_status(description="Measuring soil height...") 

150 

151 measure_soil_height_message = { 

152 "kind": "execute_script", 

153 "args": { 

154 "label": "Measure Soil Height" 

155 } 

156 } 

157 

158 self.broker.publish(measure_soil_height_message) 

159 

160 def read_status(self, path=None): 

161 """Returns the FarmBot status tree.""" 

162 path_str = "" if path is None else f" of {path}" 

163 self.state.print_status(description=f"Reading status{path_str}...") 

164 status_message = { 

165 "kind": "read_status", 

166 "args": {} 

167 } 

168 self.broker.publish(status_message) 

169 

170 status_trees = self.state.last_messages.get("status", []) 

171 status_tree = None if len(status_trees) == 0 else status_trees[-1] 

172 

173 if path is not None: 

174 for key in path.split("."): 

175 status_tree = status_tree[key] 

176 

177 self.state.print_status(update_only=True, endpoint_json=status_tree) 

178 return status_tree 

179 

180 @staticmethod 

181 def convert_mode_to_number(mode): 

182 """Converts mode string to mode number.""" 

183 modes = ["digital", "analog"] 

184 if str(mode).lower() not in modes: 

185 raise ValueError(f"Invalid mode: {mode} not in {modes}") 

186 return 0 if mode.lower() == "digital" else 1 

187 

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

189 """Reads the given pin by number.""" 

190 pin_mode = self.convert_mode_to_number(mode) 

191 self.state.print_status( 

192 description=f"Reading pin {pin_number} ({mode})...") 

193 read_pin_message = { 

194 "kind": "read_pin", 

195 "args": { 

196 "pin_number": pin_number, 

197 "label": "---", 

198 "pin_mode": pin_mode, 

199 } 

200 } 

201 self.broker.publish(read_pin_message) 

202 

203 def read_sensor(self, sensor_name): 

204 """Reads the given sensor.""" 

205 self.state.print_status(description=f"Reading {sensor_name} sensor...") 

206 sensor = self.get_resource_by_name("sensors", sensor_name) 

207 if sensor is None: 

208 return 

209 sensor_id = sensor["id"] 

210 mode = sensor["mode"] 

211 

212 sensor_message = { 

213 "kind": "read_pin", 

214 "args": { 

215 "pin_mode": mode, 

216 "label": "---", 

217 "pin_number": { 

218 "kind": "named_pin", 

219 "args": { 

220 "pin_type": "Sensor", 

221 "pin_id": sensor_id, 

222 } 

223 } 

224 } 

225 } 

226 

227 self.broker.publish(sensor_message) 

228 

229 def get_resource_by_name(self, endpoint, resource_name, name_key="label", query=None): 

230 """Find a resource by name.""" 

231 self.state.print_status( 

232 description=f"Searching for {resource_name} in {endpoint}.") 

233 resources = self.state.fetch_cache(endpoint) 

234 if resources is None: 

235 resources = self.api_get(endpoint, data_print=False) 

236 else: 

237 self.state.print_status( 

238 description=f"Using {len(resources)} cached items.") 

239 if query is not None: 

240 for key, value in query.items(): 

241 resources = [res for res in resources if res[key] == value] 

242 names = [resource[name_key] for resource in resources] 

243 if resource_name not in names: 

244 error = f"ERROR: '{resource_name}' not in {endpoint}: {names}." 

245 self.state.print_status(description=error, update_only=True) 

246 self.state.error = error 

247 self.state.clear_cache(endpoint) 

248 return None 

249 

250 self.state.save_cache(endpoint, resources) 

251 resource = [p for p in resources if p[name_key] == resource_name][0] 

252 return resource