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

109 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-09-04 17:38 -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 

22class Information(): 

23 """Information class.""" 

24 def __init__(self, state): 

25 self.broker = BrokerConnect(state) 

26 self.api = ApiConnect(state) 

27 self.state = state 

28 

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

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

31 self.state.print_status(description=f"Retrieving {endpoint} information.") 

32 

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

34 

35 if data_print: 

36 self.state.print_status(update_only=True, endpoint_json=endpoint_data) 

37 else: 

38 self.state.print_status(update_only=True, description=f"Fetched {len(endpoint_data)} items.") 

39 

40 return endpoint_data 

41 

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

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

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

45 

46 result = self.api.request("PATCH", endpoint, database_id=database_id, payload=new_data) 

47 

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

49 

50 return result 

51 

52 def api_post(self, endpoint, new_data): 

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

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

55 

56 result = self.api.request("POST", endpoint, database_id=None, payload=new_data) 

57 

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

59 

60 return result 

61 

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

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

64 self.state.print_status(description=f"Deleting {endpoint} with id={database_id}.") 

65 

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

67 

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

69 

70 return result 

71 

72 def safe_z(self): 

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

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

75 

76 config_data = self.api_get('fbos_config') 

77 z_value = config_data["safe_height"] 

78 

79 self.state.print_status(description=f"Safe z={z_value}", update_only=True) 

80 return z_value 

81 

82 def garden_size(self): 

83 """Return size of garden bed.""" 

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

85 

86 json_data = self.api_get('firmware_config') 

87 

88 x_steps = json_data['movement_axis_nr_steps_x'] 

89 x_mm = json_data['movement_step_per_mm_x'] 

90 

91 y_steps = json_data['movement_axis_nr_steps_y'] 

92 y_mm = json_data['movement_step_per_mm_y'] 

93 

94 z_steps = json_data['movement_axis_nr_steps_z'] 

95 z_mm = json_data['movement_step_per_mm_z'] 

96 

97 garden_size = { 

98 "x": x_steps / x_mm, 

99 "y": y_steps / y_mm, 

100 "z": z_steps / z_mm, 

101 } 

102 

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

104 return garden_size 

105 

106 def group(self, group_id=None): 

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

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

109 

110 if group_id is None: 

111 group_data = self.api_get("point_groups") 

112 else: 

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

114 

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

116 return group_data 

117 

118 def curve(self, curve_id=None): 

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

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

121 

122 if curve_id is None: 

123 curve_data = self.api_get("curves") 

124 else: 

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

126 

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

128 return curve_data 

129 

130 def measure_soil_height(self): 

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

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

133 

134 measure_soil_height_message = { 

135 "kind": "execute_script", 

136 "args": { 

137 "label": "Measure Soil Height" 

138 } 

139 } 

140 

141 self.broker.publish(measure_soil_height_message) 

142 

143 def read_status(self): 

144 """Returns the FarmBot status tree.""" 

145 self.state.print_status(description="Reading status...") 

146 status_message = { 

147 "kind": "read_status", 

148 "args": {} 

149 } 

150 self.broker.publish(status_message) 

151 

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

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

154 

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

156 return status_tree 

157 

158 @staticmethod 

159 def convert_mode_to_string(mode): 

160 """Converts mode to string.""" 

161 return "digital" if mode == 0 else "analog" 

162 

163 def read_pin(self, pin_number, mode=0): 

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

165 mode_str = self.convert_mode_to_string(mode) 

166 self.state.print_status(description=f"Reading pin {pin_number} ({mode_str})...") 

167 read_pin_message = { 

168 "kind": "read_pin", 

169 "args": { 

170 "pin_number": pin_number, 

171 "label": "---", 

172 "pin_mode": mode, 

173 } 

174 } 

175 self.broker.publish(read_pin_message) 

176 

177 def read_sensor(self, sensor_name): 

178 """Reads the given sensor.""" 

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

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

181 if sensor is None: 

182 return 

183 sensor_id = sensor["id"] 

184 mode = sensor["mode"] 

185 

186 sensor_message = { 

187 "kind": "read_pin", 

188 "args": { 

189 "pin_mode": mode, 

190 "label": "---", 

191 "pin_number": { 

192 "kind": "named_pin", 

193 "args": { 

194 "pin_type": "Sensor", 

195 "pin_id": sensor_id, 

196 } 

197 } 

198 } 

199 } 

200 

201 self.broker.publish(sensor_message) 

202 

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

204 """Find a resource by name.""" 

205 self.state.print_status(description=f"Searching for {resource_name} in {endpoint}.") 

206 resources = self.state.fetch_cache(endpoint) 

207 if resources is None: 

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

209 else: 

210 self.state.print_status(description=f"Using {len(resources)} cached items.") 

211 if query is not None: 

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

213 resources = [resource for resource in resources if resource[key] == value] 

214 resource_names = [resource[name_key] for resource in resources] 

215 if resource_name not in resource_names: 

216 error = f"ERROR: '{resource_name}' not in {endpoint}: {resource_names}." 

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

218 self.state.error = error 

219 self.state.clear_cache(endpoint) 

220 return None 

221 

222 self.state.save_cache(endpoint, resources) 

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

224 return resource