4This module sets up a Flask web application for managing storage items, controlling WLED devices,
5interacting with a WiFi scale, and querying an LLM service. It provides various routes for rendering templates,
6handling item creation, deletion, and search, as well as retrieving environment configurations,
7scale weight, and logging messages.
10from typing
import Annotated
21from flask_cors
import CORS
22from dotenv
import load_dotenv
24from logger_config
import logger
25import Storage_connector
27import weigh_fi_manager
as wifiscale
29import ollama_manager
as ollama
31ENV_PATH = os.path.join(os.path.dirname(__file__),
"data/.env")
32load_dotenv(dotenv_path=ENV_PATH)
33IS_SCALE_ENABLED = os.getenv(
"IS_SCALE_ENABLED").lower() ==
"true"
39def index() -> Annotated[str, "search page as a rendered template"]:
41 Renders the search page.
43 Response: The rendered HTML template for the search page.
45 return render_template(
"search.html")
48@app.route("/toggleLight")
49def toggle_light() -> Annotated[str, "search page as a rendered template"]:
51 Toggles the power state of the WLED device and renders the search template.
52 This function changes the power state of the WLED device to the opposite of its current state
53 by calling the `changePowerState` method of the `wled_requests` object.
54 After toggling the power state,
55 it returns the rendered "search.html" template.
57 str: The rendered "search.html" template.
61 if current_state
is not None:
63 logger.info(f
"Toggled WLED power state to: {not current_state}")
65 logger.warning(
"Unable to toggle WLED power state: Current state is unknown.")
66 except Exception
as e:
67 logger.error(f
"Error toggling WLED power state: {e}")
68 return render_template(
"search.html")
71@app.route("/createItem")
72def create_item() -> Annotated[str, "item creation page as a rendered template"]:
74 Renders the template for creating a new item.
76 Response: The rendered HTML template for creating a new item.
78 return render_template(
"createItem.html")
81@app.route("/sendCreation", methods=["POST"])
82def send_creation() -> Annotated[tuple, {"status": str,
"status_code": int}]:
84 Handle the creation of a new item by processing the incoming JSON request data.
85 The function expects a JSON payload with the following structure:
92 It extracts the necessary information from the JSON payload and attempts to create a new item
93 using the Storage_connector.CreateItem method.
94 If an exception occurs during the creation process,
95 it prints the exception.
97 tuple: A dictionary with a status message and an HTTP status code.
100 data = request.get_json()
101 logger.info(f
"Received creation request with data: {data}")
102 info = json.dumps(data[
"info"])
103 obj_type = data[
"type"]
105 pos = data[
"position"]
108 pos=pos, obj_type=obj_type, name=name, json_data=info
110 except Exception
as e:
112 f
"Failed to create item with name: {name}, type: {obj_type}, position: {pos}. Error: {e}"
114 return {
"status":
"error"}, 500
115 return {
"status":
"created"}, 201
118@app.route("/item/<item_id>")
119def item(item_id) -> Annotated[str, "item page as a rendered template"]:
121 Handles the request to display an item.
122 This function performs the following steps:
123 1. Changes the power state of the WLED device to on.
124 2. Fetches the item details from the storage using the provided item identifier.
125 3. Sets the color position on the WLED device based on the fetched item details.
126 4. If the fetched item contains additional information,
127 it parses the data and renders the 'item.jinja2' template
128 with the item details and information.
129 6. If the fetched item does not contain additional information,
130 it renders the 'item.jinja2' template with only the item details.
132 item_id (int): The id of the item to display.
134 The rendered HTML template for the item.
139 logger.info(f
"Fetched item details for item_id {item_id}: {item_sql}")
141 json_info = json.loads(item_sql[4])
142 return render_template(
"item.jinja2", item=item_sql, json=json_info, id=item_id)
144 return render_template(
"item.jinja2", item=item_sql, id=item_id)
147@app.route("/item/<item_id>/update", methods=["POST"])
148def update_item(item_id) -> Annotated[tuple, {"status": str,
"status_code": int}]:
150 Updates an item using the Storage_connector and returns a status message.
152 item_id: The id of the item to be updated.
154 A dictionary with a status message and an HTTP status code.
156 data = request.get_json()
157 logger.info(f
"Received update request for item_id {item_id} with data: {data}")
158 info = json.dumps(data.get(
"info", {}))
159 obj_type = data.get(
"type")
160 name = data.get(
"name")
161 pos = data.get(
"position")
164 item_id=item_id, pos=pos, obj_type=obj_type, name=name, json_data=info
166 except Exception
as e:
167 logger.error(f
"Failed to update item with id: {item_id}. Error: {e}")
168 return {
"status":
"error"}, 500
169 return {
"status":
"updated"}, 200
172@app.route("/item/<item>/delete")
173def delete_item(item_id) -> Annotated[str, "search page as a rendered template"]:
175 Deletes an item using the Storage_connector and renders the search.html template.
177 item_id: The id of the item to be deleted.
179 A rendered template for the search page.
183 return render_template(
"search.html")
186@app.route("/search/<term>", methods=["GET"])
189 Search for a term in the storage and return the results in JSON format.
191 term (str): The term to search for in the storage.
193 Response: A Flask Response object containing the search results in JSON format.
199@app.errorhandler(Exception)
202 Handles exceptions by passing through HTTP errors.
204 e (Exception): The exception to handle.
206 Exception: The same exception that was passed in.
213@app.route("/config/env")
216 Retrieve the environment configuration.
217 This function uses the config_manager to get the current environment
218 configuration and returns it as a JSON response.
220 Response: A Flask JSON response containing the environment configuration.
225@app.route("/config/ollama/models")
228 Fetches the list of available Ollama models.
230 Annotated[str, "json response"]: A JSON response containing the list of Ollama models.
232 values = ollama.get_ollama_models()
234 return jsonify({
"status":
"Ollama service is not enabled"}), 412
235 return jsonify(values)
238@app.route("/wifiscale/weight")
241 Retrieve the weight of the scale.
242 This function checks if the scale service is enabled by reading the IS_SCALE_ENABLED environment variable.
243 If the scale service is not enabled, it returns a JSON response with a status message and HTTP status code 412.
244 If the scale service is enabled, it uses the wifiscale module to get the weight of the scale and returns it as a JSON response.
246 Response: A Flask JSON response containing the weight of the scale or a status message.
248 if IS_SCALE_ENABLED ==
"False":
249 return jsonify({
"status":
"scale service is not enabled"}), 412
251 weight = wifiscale.get_weight()
252 return jsonify({
"weight": weight})
255@app.route("/llm/ask", methods=["POST"])
258 Ask a question to the Language Learning Model (LLM) service.
259 This function sends a GET request to the LLM service at the specified endpoint
260 and returns the response as a JSON object.
262 Response: A Flask JSON response containing the response from the LLM service.
264 data = request.get_json()
265 question = data.get(
"question")
266 logger.info(f
"Received question via llm endpoint: {question}")
267 response = ollama.ask_question(question)
268 return jsonify(response)
271@app.route("/log", methods=["POST"])
272def log_message() -> Annotated[tuple, {"status": str,
"status_code": int}]:
274 Logs a message with a specified log level.
276 The log level and message content are extracted from the JSON payload of the request.
277 If the log level is not provided, it defaults to 'INFO'.
278 If the message content is not provided, it defaults to an empty string.
281 tuple: A dictionary with a status message and an HTTP status code 201.
283 Request JSON structure:
285 "level": "DEBUG" | "INFO" | "WARNING" | "ERROR" | "CRITICAL",
286 "message": "Your log message here"
290 data: Annotated[str,
"content of request"] = request.json
291 level: Annotated[str,
"log level, default value is INFO"] = data.get(
294 message: Annotated[str,
"content of log, default value is empty"] = data.get(
300 logger.debug(message)
304 logger.warning(message)
306 logger.error(message)
308 logger.critical(message)
310 return {
"status":
"created"}, 201
313with app.app_context():
317if __name__ ==
"__main__":
318 app.run(host=
"0.0.0.0", debug=
True)
List[StorageItem] search(str search_term)
StorageItem|None fetch_item(int item_id)
None update_item(int item_id, int pos, StorageItemType obj_type, str name, str json_data)
None create_item(int pos, StorageItemType obj_type, str name, str json_data)
None delete_item(int item_id)
Response ask_llm_question()
Exception handle_exception(e)
Annotated[tuple, {"status":str, "status_code":int}] log_message()
Annotated[tuple, {"status":str, "status_code":int}] send_creation()
Annotated[tuple, {"status":str, "status_code":int}] update_item(item_id)
tuple[Response, int]|Response get_ollama_models()
Annotated[str, "item creation page as a rendered template"] create_item()
tuple[Response, int]|Response get_weight()
Annotated[str, "search page as a rendered template"] toggle_light()
Annotated[str, "search page as a rendered template"] delete_item(item_id)
Annotated[str, "item page as a rendered template"] item(item_id)
Annotated[str, "search page as a rendered template"] index()
Annotated[dict, "dictionary of environment variables"] get_env()
None change_power_state(state)