From f3f72eb274203f3eff173890a5d49ee09918c724 Mon Sep 17 00:00:00 2001 From: Marco Franke Date: Wed, 4 Oct 2023 15:40:47 +0000 Subject: [PATCH] feat: Spoolman version in device info closes #25 refactor: Wrapped Spoolman api in a wrapper --- .vscode/extensions.json | 16 + custom_components/spoolman/__init__.py | 35 +- .../spoolman/classes/openapi.json | 2167 +++++++++++++++++ .../spoolman/classes/spoolman_api.py | 68 + custom_components/spoolman/config_flow.py | 52 +- custom_components/spoolman/const.py | 7 +- custom_components/spoolman/coordinator.py | 23 +- custom_components/spoolman/sensor.py | 33 +- 8 files changed, 2321 insertions(+), 80 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 custom_components/spoolman/classes/openapi.json create mode 100644 custom_components/spoolman/classes/spoolman_api.py diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..8ec257f --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,16 @@ +{ + "recommendations": [ + "ms-python.black-formatter", + "vadimcn.vscode-lldb", + "vivaxy.vscode-conventional-commits", + "ryanluker.vscode-coverage-gutters", + "serayuzgur.crates", + "dbaeumer.vscode-eslint", + "tamasfe.even-better-toml", + "github.vscode-pull-request-github", + "eamodio.gitlens", + "ms-python.vscode-pylance", + "ms-python.python", + "charliermarsh.ruff" + ] +} diff --git a/custom_components/spoolman/__init__.py b/custom_components/spoolman/__init__.py index f90d017..9bfcb0a 100644 --- a/custom_components/spoolman/__init__.py +++ b/custom_components/spoolman/__init__.py @@ -1,14 +1,13 @@ """Spoolman home assistant integration.""" import logging -import aiohttp -from homeassistant.const import Platform, CONF_NAME -from homeassistant.core import HomeAssistant -from homeassistant.components.sensor import PLATFORM_SCHEMA import homeassistant.helpers.config_validation as cv import voluptuous as vol +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import CONF_NAME, Platform +from homeassistant.core import HomeAssistant -from .const import DOMAIN, CONF_URL, DEFAULT_NAME +from .const import CONF_URL, DEFAULT_NAME, DOMAIN, SPOOLMAN_API_WRAPPER from .coordinator import SpoolManCoordinator _LOGGER = logging.getLogger(__name__) @@ -40,6 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry): """Set up the Spoolman component from a config entry.""" _LOGGER.debug("__init__.async_setup_entry") # session = async_create_clientsession(hass) + coordinator = SpoolManCoordinator(hass, entry) await coordinator.async_refresh() await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -58,14 +58,17 @@ async def async_unload_entry(hass: HomeAssistant, entry): async def async_get_data(hass: HomeAssistant): """Get the latest data from the Spoolman API.""" _LOGGER.debug("__init__.async_get_data") - url = hass.data[DOMAIN][CONF_URL] - - async with aiohttp.ClientSession() as session: - try: - headers = {} - - async with session.get(url, headers=headers) as response: - data = await response.json() - return data - except Exception as ex: - _LOGGER.error("Error fetching data from Spoolman API: %s", ex) + # url = hass.data[DOMAIN][CONF_URL] + return await hass.data[DOMAIN][SPOOLMAN_API_WRAPPER].get_spool( + {"allow_archived": False} + ) + + # async with aiohttp.ClientSession() as session: + # try: + # headers = {} + + # async with session.get(url, headers=headers) as response: + # data = await response.json() + # return data + # except Exception as ex: + # _LOGGER.error("Error fetching data from Spoolman API: %s", ex) diff --git a/custom_components/spoolman/classes/openapi.json b/custom_components/spoolman/classes/openapi.json new file mode 100644 index 0000000..27ddbb0 --- /dev/null +++ b/custom_components/spoolman/classes/openapi.json @@ -0,0 +1,2167 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Spoolman REST API v1", + "version": "1.0.0" + }, + "paths": { + "/info": { + "get": { + "summary": "Info", + "description": "Return general info about the API.", + "operationId": "info_info_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Info" + } + } + } + } + } + } + }, + "/health": { + "get": { + "summary": "Health", + "description": "Return a health check.", + "operationId": "health_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HealthCheck" + } + } + } + } + } + } + }, + "/backup": { + "post": { + "summary": "Backup", + "description": "Trigger a database backup. Only applicable for SQLite databases.", + "operationId": "backup_backup_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BackupResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + } + } + } + }, + "/filament": { + "get": { + "tags": [ + "filament" + ], + "summary": "Find Filaments", + "description": "Get a list of filaments that matches the search query.", + "operationId": "Find_filaments_filament_get", + "parameters": [ + { + "description": "See vendor.name.", + "required": false, + "deprecated": true, + "schema": { + "type": "string", + "title": "Vendor Name", + "description": "See vendor.name." + }, + "name": "vendor_name", + "in": "query" + }, + { + "description": "See vendor.id.", + "required": false, + "deprecated": true, + "schema": { + "type": "string", + "title": "Vendor ID", + "description": "See vendor.id." + }, + "name": "vendor_id", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the filament vendor name. Separate multiple terms with a comma. Specify an empty string to match filaments with no vendor name.", + "required": false, + "schema": { + "type": "string", + "title": "Vendor Name", + "description": "Partial case-insensitive search term for the filament vendor name. Separate multiple terms with a comma. Specify an empty string to match filaments with no vendor name." + }, + "name": "vendor.name", + "in": "query" + }, + { + "description": "Match an exact vendor ID. Separate multiple IDs with a comma. Specify -1 to match filaments with no vendor.", + "required": false, + "schema": { + "type": "string", + "title": "Vendor ID", + "description": "Match an exact vendor ID. Separate multiple IDs with a comma. Specify -1 to match filaments with no vendor.", + "examples": [ + "1", + "1,2" + ] + }, + "name": "vendor.id", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. Specify an empty string to match filaments with no name.", + "required": false, + "schema": { + "type": "string", + "title": "Filament Name", + "description": "Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. Specify an empty string to match filaments with no name." + }, + "name": "name", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. Specify an empty string to match filaments with no material.", + "required": false, + "schema": { + "type": "string", + "title": "Filament Material", + "description": "Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. Specify an empty string to match filaments with no material." + }, + "name": "material", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the filament article number. Separate multiple terms with a comma. Specify an empty string to match filaments with no article number.", + "required": false, + "schema": { + "type": "string", + "title": "Filament Article Number", + "description": "Partial case-insensitive search term for the filament article number. Separate multiple terms with a comma. Specify an empty string to match filaments with no article number." + }, + "name": "article_number", + "in": "query" + }, + { + "description": "Sort the results by the given field. Should be a comma-separate string with \"field:direction\" items.", + "required": false, + "schema": { + "type": "string", + "title": "Sort", + "description": "Sort the results by the given field. Should be a comma-separate string with \"field:direction\" items." + }, + "example": "vendor.name:asc,spool_weight:desc", + "name": "sort", + "in": "query" + }, + { + "description": "Maximum number of items in the response.", + "required": false, + "schema": { + "type": "integer", + "title": "Limit", + "description": "Maximum number of items in the response." + }, + "name": "limit", + "in": "query" + }, + { + "description": "Offset in the full result set if a limit is set.", + "required": false, + "schema": { + "type": "integer", + "title": "Offset", + "description": "Offset in the full result set if a limit is set.", + "default": 0 + }, + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Filament" + }, + "type": "array", + "title": "Response Find Filaments Filament Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "filament" + ], + "summary": "Add Filament", + "description": "Add a new filament to the database.", + "operationId": "Add_filament_filament_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FilamentParameters" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Filament" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/filament/{filament_id}": { + "get": { + "tags": [ + "filament" + ], + "summary": "Get Filament", + "description": "Get a specific filament.", + "operationId": "Get_filament_filament__filament_id__get", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Filament Id" + }, + "name": "filament_id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Filament" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "filament" + ], + "summary": "Delete Filament", + "description": "Delete a filament.", + "operationId": "Delete_filament_filament__filament_id__delete", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Filament Id" + }, + "name": "filament_id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "patch": { + "tags": [ + "filament" + ], + "summary": "Update Filament", + "description": "Update any attribute of a filament. Only fields specified in the request will be affected.", + "operationId": "Update_filament_filament__filament_id__patch", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Filament Id" + }, + "name": "filament_id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FilamentUpdateParameters" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Filament" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/spool": { + "get": { + "tags": [ + "spool" + ], + "summary": "Find Spool", + "description": "Get a list of spools that matches the search query.", + "operationId": "Find_spool_spool_get", + "parameters": [ + { + "description": "See filament.name.", + "required": false, + "deprecated": true, + "schema": { + "type": "string", + "title": "Filament Name", + "description": "See filament.name." + }, + "name": "filament_name", + "in": "query" + }, + { + "description": "See filament.id.", + "required": false, + "deprecated": true, + "schema": { + "type": "string", + "title": "Filament ID", + "description": "See filament.id." + }, + "name": "filament_id", + "in": "query" + }, + { + "description": "See filament.material.", + "required": false, + "deprecated": true, + "schema": { + "type": "string", + "title": "Filament Material", + "description": "See filament.material." + }, + "name": "filament_material", + "in": "query" + }, + { + "description": "See filament.vendor.name.", + "required": false, + "deprecated": true, + "schema": { + "type": "string", + "title": "Vendor Name", + "description": "See filament.vendor.name." + }, + "name": "vendor_name", + "in": "query" + }, + { + "description": "See filament.vendor.id.", + "required": false, + "deprecated": true, + "schema": { + "type": "string", + "title": "Vendor ID", + "description": "See filament.vendor.id." + }, + "name": "vendor_id", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. Specify an empty string to match spools with no filament name.", + "required": false, + "schema": { + "type": "string", + "title": "Filament Name", + "description": "Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. Specify an empty string to match spools with no filament name." + }, + "name": "filament.name", + "in": "query" + }, + { + "description": "Match an exact filament ID. Separate multiple IDs with a comma.", + "required": false, + "schema": { + "type": "string", + "title": "Filament ID", + "description": "Match an exact filament ID. Separate multiple IDs with a comma.", + "examples": [ + "1", + "1,2" + ] + }, + "name": "filament.id", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. Specify an empty string to match spools with no filament material.", + "required": false, + "schema": { + "type": "string", + "title": "Filament Material", + "description": "Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. Specify an empty string to match spools with no filament material." + }, + "name": "filament.material", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the filament vendor name. Separate multiple terms with a comma. Specify an empty string to match spools with no vendor name.", + "required": false, + "schema": { + "type": "string", + "title": "Vendor Name", + "description": "Partial case-insensitive search term for the filament vendor name. Separate multiple terms with a comma. Specify an empty string to match spools with no vendor name." + }, + "name": "filament.vendor.name", + "in": "query" + }, + { + "description": "Match an exact vendor ID. Separate multiple IDs with a comma. Set it to -1 to match spools with filaments with no vendor.", + "required": false, + "schema": { + "type": "string", + "title": "Vendor ID", + "description": "Match an exact vendor ID. Separate multiple IDs with a comma. Set it to -1 to match spools with filaments with no vendor.", + "examples": [ + "1", + "1,2" + ] + }, + "name": "filament.vendor.id", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the spool location. Separate multiple terms with a comma. Specify an empty string to match spools with no location.", + "required": false, + "schema": { + "type": "string", + "title": "Location", + "description": "Partial case-insensitive search term for the spool location. Separate multiple terms with a comma. Specify an empty string to match spools with no location." + }, + "name": "location", + "in": "query" + }, + { + "description": "Partial case-insensitive search term for the spool lot number. Separate multiple terms with a comma. Specify an empty string to match spools with no lot nr.", + "required": false, + "schema": { + "type": "string", + "title": "Lot/Batch Number", + "description": "Partial case-insensitive search term for the spool lot number. Separate multiple terms with a comma. Specify an empty string to match spools with no lot nr." + }, + "name": "lot_nr", + "in": "query" + }, + { + "description": "Whether to include archived spools in the search results.", + "required": false, + "schema": { + "type": "boolean", + "title": "Allow Archived", + "description": "Whether to include archived spools in the search results.", + "default": false + }, + "name": "allow_archived", + "in": "query" + }, + { + "description": "Sort the results by the given field. Should be a comma-separate string with \"field:direction\" items.", + "required": false, + "schema": { + "type": "string", + "title": "Sort", + "description": "Sort the results by the given field. Should be a comma-separate string with \"field:direction\" items." + }, + "example": "filament.name:asc,filament.vendor.id:asc,location:desc", + "name": "sort", + "in": "query" + }, + { + "description": "Maximum number of items in the response.", + "required": false, + "schema": { + "type": "integer", + "title": "Limit", + "description": "Maximum number of items in the response." + }, + "name": "limit", + "in": "query" + }, + { + "description": "Offset in the full result set if a limit is set.", + "required": false, + "schema": { + "type": "integer", + "title": "Offset", + "description": "Offset in the full result set if a limit is set.", + "default": 0 + }, + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Spool" + }, + "type": "array", + "title": "Response Find Spool Spool Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "spool" + ], + "summary": "Add Spool", + "description": "Add a new spool to the database. Only specify either remaining_weight or used_weight. If no weight is set, the spool will be assumed to be full.", + "operationId": "Add_spool_spool_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SpoolParameters" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Spool" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/spool/{spool_id}": { + "get": { + "tags": [ + "spool" + ], + "summary": "Get Spool", + "description": "Get a specific spool. A websocket is served on the same path to listen for changes to the spool. The response model is the same for the websocket messages as for this endpoint.", + "operationId": "Get_spool_spool__spool_id__get", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Spool Id" + }, + "name": "spool_id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Spool" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "spool" + ], + "summary": "Delete Spool", + "description": "Delete a spool.", + "operationId": "Delete_spool_spool__spool_id__delete", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Spool Id" + }, + "name": "spool_id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "patch": { + "tags": [ + "spool" + ], + "summary": "Update Spool", + "description": "Update any attribute of a spool. Only fields specified in the request will be affected. remaining_weight and used_weight can't be set at the same time.", + "operationId": "Update_spool_spool__spool_id__patch", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Spool Id" + }, + "name": "spool_id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SpoolUpdateParameters" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Spool" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/spool/{spool_id}/use": { + "put": { + "tags": [ + "spool" + ], + "summary": "Use Spool Filament", + "description": "Use some length or weight of filament from the spool. Specify either a length or a weight, not both.", + "operationId": "Use_spool_filament_spool__spool_id__use_put", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Spool Id" + }, + "name": "spool_id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SpoolUseParameters" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Spool" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/vendor": { + "get": { + "tags": [ + "vendor" + ], + "summary": "Find Vendor", + "description": "Get a list of vendors that matches the search query.", + "operationId": "Find_vendor_vendor_get", + "parameters": [ + { + "description": "Partial case-insensitive search term for the vendor name. Separate multiple terms with a comma.", + "required": false, + "schema": { + "type": "string", + "title": "Vendor Name", + "description": "Partial case-insensitive search term for the vendor name. Separate multiple terms with a comma." + }, + "name": "name", + "in": "query" + }, + { + "description": "Sort the results by the given field. Should be a comma-separate string with \"field:direction\" items.", + "required": false, + "schema": { + "type": "string", + "title": "Sort", + "description": "Sort the results by the given field. Should be a comma-separate string with \"field:direction\" items." + }, + "example": "name:asc,id:desc", + "name": "sort", + "in": "query" + }, + { + "description": "Maximum number of items in the response.", + "required": false, + "schema": { + "type": "integer", + "title": "Limit", + "description": "Maximum number of items in the response." + }, + "name": "limit", + "in": "query" + }, + { + "description": "Offset in the full result set if a limit is set.", + "required": false, + "schema": { + "type": "integer", + "title": "Offset", + "description": "Offset in the full result set if a limit is set.", + "default": 0 + }, + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Vendor" + }, + "type": "array", + "title": "Response Find Vendor Vendor Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "vendor" + ], + "summary": "Add Vendor", + "description": "Add a new vendor to the database.", + "operationId": "Add_vendor_vendor_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VendorParameters" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Vendor" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/vendor/{vendor_id}": { + "get": { + "tags": [ + "vendor" + ], + "summary": "Get Vendor", + "description": "Get a specific vendor.", + "operationId": "Get_vendor_vendor__vendor_id__get", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Vendor Id" + }, + "name": "vendor_id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Vendor" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "vendor" + ], + "summary": "Delete Vendor", + "description": "Delete a vendor. The vendor attribute of any filaments who refer to the deleted vendor will be cleared.", + "operationId": "Delete_vendor_vendor__vendor_id__delete", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Vendor Id" + }, + "name": "vendor_id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "patch": { + "tags": [ + "vendor" + ], + "summary": "Update Vendor", + "description": "Update any attribute of a vendor. Only fields specified in the request will be affected.", + "operationId": "Update_vendor_vendor__vendor_id__patch", + "parameters": [ + { + "required": true, + "schema": { + "type": "integer", + "title": "Vendor Id" + }, + "name": "vendor_id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VendorUpdateParameters" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Vendor" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/material": { + "get": { + "tags": [ + "other" + ], + "summary": "Find Materials", + "description": "Get a list of all filament materials.", + "operationId": "Find_materials_material_get", + "responses": { + "200": { + "description": "A list of all filament materials.", + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Response Find Materials Material Get" + }, + "example": [ + "PLA", + "ABS", + "PETG" + ] + } + } + } + } + } + }, + "/article-number": { + "get": { + "tags": [ + "other" + ], + "summary": "Find Article Numbers", + "description": "Get a list of all article numbers.", + "operationId": "Find_article_numbers_article_number_get", + "responses": { + "200": { + "description": "A list of all article numbers.", + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Response Find Article Numbers Article Number Get" + }, + "example": [ + "123456", + "987654" + ] + } + } + } + } + } + }, + "/lot-number": { + "get": { + "tags": [ + "other" + ], + "summary": "Find Lot Numbers", + "description": "Get a list of all lot numbers.", + "operationId": "Find_lot_numbers_lot_number_get", + "responses": { + "200": { + "description": "A list of all lot numbers.", + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Response Find Lot Numbers Lot Number Get" + }, + "example": [ + "123456", + "987654" + ] + } + } + } + } + } + }, + "/location": { + "get": { + "tags": [ + "other" + ], + "summary": "Find Locations", + "description": "Get a list of all spool locations.", + "operationId": "Find_locations_location_get", + "responses": { + "200": { + "description": "A list of all spool locations.", + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Response Find Locations Location Get" + }, + "example": [ + "Printer 1", + "Printer 2", + "Storage Shelf A" + ] + } + } + } + } + } + } + }, + "components": { + "schemas": { + "BackupResponse": { + "properties": { + "path": { + "type": "string", + "title": "Path", + "description": "Path to the created backup file.", + "example": "/home/app/.local/share/spoolman/backups/spoolman.db" + } + }, + "type": "object", + "title": "BackupResponse" + }, + "Filament": { + "properties": { + "id": { + "type": "integer", + "title": "Id", + "description": "Unique internal ID of this filament type." + }, + "registered": { + "type": "string", + "format": "date-time", + "title": "Registered", + "description": "When the filament was registered in the database. UTC Timezone." + }, + "name": { + "type": "string", + "maxLength": 64, + "title": "Name", + "description": "Filament name, to distinguish this filament type among others from the same vendor.Should contain its color for example.", + "example": "PolyTerra™ Charcoal Black" + }, + "vendor": { + "allOf": [ + { + "$ref": "#/components/schemas/Vendor" + } + ], + "title": "Vendor", + "description": "The vendor of this filament type." + }, + "material": { + "type": "string", + "maxLength": 64, + "title": "Material", + "description": "The material of this filament, e.g. PLA.", + "example": "PLA" + }, + "price": { + "type": "number", + "minimum": 0, + "title": "Price", + "description": "The price of this filament in the system configured currency.", + "example": 20 + }, + "density": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Density", + "description": "The density of this filament in g/cm3.", + "example": 1.24 + }, + "diameter": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Diameter", + "description": "The diameter of this filament in mm.", + "example": 1.75 + }, + "weight": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Weight", + "description": "The weight of the filament in a full spool, in grams.", + "example": 1000 + }, + "spool_weight": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Spool Weight", + "description": "The empty spool weight, in grams.", + "example": 140 + }, + "article_number": { + "type": "string", + "maxLength": 64, + "title": "Article Number", + "description": "Vendor article number, e.g. EAN, QR code, etc.", + "example": "PM70820" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this filament type.", + "example": "" + }, + "settings_extruder_temp": { + "type": "integer", + "minimum": 0, + "title": "Settings Extruder Temp", + "description": "Overridden extruder temperature, in °C.", + "example": 210 + }, + "settings_bed_temp": { + "type": "integer", + "minimum": 0, + "title": "Settings Bed Temp", + "description": "Overridden bed temperature, in °C.", + "example": 60 + }, + "color_hex": { + "type": "string", + "maxLength": 8, + "minLength": 6, + "title": "Color Hex", + "description": "Hexadecimal color code of the filament, e.g. FF0000 for red. Supports alpha channel at the end.", + "example": "FF0000" + } + }, + "type": "object", + "required": [ + "id", + "registered", + "density", + "diameter" + ], + "title": "Filament" + }, + "FilamentParameters": { + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "title": "Name", + "description": "Filament name, to distinguish this filament type among others from the same vendor.Should contain its color for example.", + "example": "PolyTerra™ Charcoal Black" + }, + "vendor_id": { + "type": "integer", + "title": "Vendor Id", + "description": "The ID of the vendor of this filament type." + }, + "material": { + "type": "string", + "maxLength": 64, + "title": "Material", + "description": "The material of this filament, e.g. PLA.", + "example": "PLA" + }, + "price": { + "type": "number", + "minimum": 0, + "title": "Price", + "description": "The price of this filament in the system configured currency.", + "example": 20 + }, + "density": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Density", + "description": "The density of this filament in g/cm3.", + "example": 1.24 + }, + "diameter": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Diameter", + "description": "The diameter of this filament in mm.", + "example": 1.75 + }, + "weight": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Weight", + "description": "The weight of the filament in a full spool, in grams. (net weight)", + "example": 1000 + }, + "spool_weight": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Spool Weight", + "description": "The empty spool weight, in grams.", + "example": 140 + }, + "article_number": { + "type": "string", + "maxLength": 64, + "title": "Article Number", + "description": "Vendor article number, e.g. EAN, QR code, etc.", + "example": "PM70820" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this filament type.", + "example": "" + }, + "settings_extruder_temp": { + "type": "integer", + "minimum": 0, + "title": "Settings Extruder Temp", + "description": "Overridden extruder temperature, in °C.", + "example": 210 + }, + "settings_bed_temp": { + "type": "integer", + "minimum": 0, + "title": "Settings Bed Temp", + "description": "Overridden bed temperature, in °C.", + "example": 60 + }, + "color_hex": { + "type": "string", + "title": "Color Hex", + "description": "Hexadecimal color code of the filament, e.g. FF0000 for red. Supports alpha channel at the end.", + "example": "FF0000" + } + }, + "type": "object", + "required": [ + "density", + "diameter" + ], + "title": "FilamentParameters" + }, + "FilamentUpdateParameters": { + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "title": "Name", + "description": "Filament name, to distinguish this filament type among others from the same vendor.Should contain its color for example.", + "example": "PolyTerra™ Charcoal Black" + }, + "vendor_id": { + "type": "integer", + "title": "Vendor Id", + "description": "The ID of the vendor of this filament type." + }, + "material": { + "type": "string", + "maxLength": 64, + "title": "Material", + "description": "The material of this filament, e.g. PLA.", + "example": "PLA" + }, + "price": { + "type": "number", + "minimum": 0, + "title": "Price", + "description": "The price of this filament in the system configured currency.", + "example": 20 + }, + "density": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Density", + "description": "The density of this filament in g/cm3.", + "example": 1.24 + }, + "diameter": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Diameter", + "description": "The diameter of this filament in mm.", + "example": 1.75 + }, + "weight": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Weight", + "description": "The weight of the filament in a full spool, in grams. (net weight)", + "example": 1000 + }, + "spool_weight": { + "type": "number", + "exclusiveMinimum": 0, + "title": "Spool Weight", + "description": "The empty spool weight, in grams.", + "example": 140 + }, + "article_number": { + "type": "string", + "maxLength": 64, + "title": "Article Number", + "description": "Vendor article number, e.g. EAN, QR code, etc.", + "example": "PM70820" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this filament type.", + "example": "" + }, + "settings_extruder_temp": { + "type": "integer", + "minimum": 0, + "title": "Settings Extruder Temp", + "description": "Overridden extruder temperature, in °C.", + "example": 210 + }, + "settings_bed_temp": { + "type": "integer", + "minimum": 0, + "title": "Settings Bed Temp", + "description": "Overridden bed temperature, in °C.", + "example": 60 + }, + "color_hex": { + "type": "string", + "title": "Color Hex", + "description": "Hexadecimal color code of the filament, e.g. FF0000 for red. Supports alpha channel at the end.", + "example": "FF0000" + } + }, + "type": "object", + "title": "FilamentUpdateParameters" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "HealthCheck": { + "properties": { + "status": { + "type": "string", + "title": "Status", + "example": "healthy" + } + }, + "type": "object", + "required": [ + "status" + ], + "title": "HealthCheck" + }, + "Info": { + "properties": { + "version": { + "type": "string", + "title": "Version", + "example": "0.7.0" + }, + "debug_mode": { + "type": "boolean", + "title": "Debug Mode", + "example": false + }, + "automatic_backups": { + "type": "boolean", + "title": "Automatic Backups", + "example": true + }, + "data_dir": { + "type": "string", + "title": "Data Dir", + "example": "/home/app/.local/share/spoolman" + }, + "backups_dir": { + "type": "string", + "title": "Backups Dir", + "example": "/home/app/.local/share/spoolman/backups" + }, + "db_type": { + "type": "string", + "title": "Db Type", + "example": "sqlite" + }, + "git_commit": { + "type": "string", + "title": "Git Commit", + "example": "a1b2c3d" + }, + "build_date": { + "type": "string", + "format": "date-time", + "title": "Build Date", + "example": "2021-01-01T00:00:00Z" + } + }, + "type": "object", + "required": [ + "version", + "debug_mode", + "automatic_backups", + "data_dir", + "backups_dir", + "db_type" + ], + "title": "Info" + }, + "Message": { + "properties": { + "message": { + "type": "string", + "title": "Message" + } + }, + "type": "object", + "required": [ + "message" + ], + "title": "Message" + }, + "Spool": { + "properties": { + "id": { + "type": "integer", + "title": "Id", + "description": "Unique internal ID of this spool of filament." + }, + "registered": { + "type": "string", + "format": "date-time", + "title": "Registered", + "description": "When the spool was registered in the database. UTC Timezone." + }, + "first_used": { + "type": "string", + "format": "date-time", + "title": "First Used", + "description": "First logged occurence of spool usage. UTC Timezone." + }, + "last_used": { + "type": "string", + "format": "date-time", + "title": "Last Used", + "description": "Last logged occurence of spool usage. UTC Timezone." + }, + "filament": { + "allOf": [ + { + "$ref": "#/components/schemas/Filament" + } + ], + "title": "Filament", + "description": "The filament type of this spool." + }, + "remaining_weight": { + "type": "number", + "minimum": 0, + "title": "Remaining Weight", + "description": "Estimated remaining weight of filament on the spool in grams. Only set if the filament type has a weight set.", + "example": 500.6 + }, + "used_weight": { + "type": "number", + "minimum": 0, + "title": "Used Weight", + "description": "Consumed weight of filament from the spool in grams.", + "example": 500.3 + }, + "remaining_length": { + "type": "number", + "minimum": 0, + "title": "Remaining Length", + "description": "Estimated remaining length of filament on the spool in millimeters. Only set if the filament type has a weight set.", + "example": 5612.4 + }, + "used_length": { + "type": "number", + "minimum": 0, + "title": "Used Length", + "description": "Consumed length of filament from the spool in millimeters.", + "example": 50.7 + }, + "location": { + "type": "string", + "maxLength": 64, + "title": "Location", + "description": "Where this spool can be found.", + "example": "Shelf A" + }, + "lot_nr": { + "type": "string", + "maxLength": 64, + "title": "Lot Nr", + "description": "Vendor manufacturing lot/batch number of the spool.", + "example": "52342" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this specific spool.", + "example": "" + }, + "archived": { + "type": "boolean", + "title": "Archived", + "description": "Whether this spool is archived and should not be used anymore." + } + }, + "type": "object", + "required": [ + "id", + "registered", + "filament", + "used_weight", + "used_length", + "archived" + ], + "title": "Spool" + }, + "SpoolParameters": { + "properties": { + "first_used": { + "type": "string", + "format": "date-time", + "title": "First Used", + "description": "First logged occurence of spool usage." + }, + "last_used": { + "type": "string", + "format": "date-time", + "title": "Last Used", + "description": "Last logged occurence of spool usage." + }, + "filament_id": { + "type": "integer", + "title": "Filament Id", + "description": "The ID of the filament type of this spool." + }, + "remaining_weight": { + "type": "number", + "minimum": 0, + "title": "Remaining Weight", + "description": "Remaining weight of filament on the spool. Can only be used if the filament type has a weight set.", + "example": 800 + }, + "used_weight": { + "type": "number", + "minimum": 0, + "title": "Used Weight", + "description": "Used weight of filament on the spool.", + "example": 200 + }, + "location": { + "type": "string", + "maxLength": 64, + "title": "Location", + "description": "Where this spool can be found.", + "example": "Shelf A" + }, + "lot_nr": { + "type": "string", + "maxLength": 64, + "title": "Lot Nr", + "description": "Vendor manufacturing lot/batch number of the spool.", + "example": "52342" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this specific spool.", + "example": "" + }, + "archived": { + "type": "boolean", + "title": "Archived", + "description": "Whether this spool is archived and should not be used anymore.", + "default": false + } + }, + "type": "object", + "required": [ + "filament_id" + ], + "title": "SpoolParameters" + }, + "SpoolUpdateParameters": { + "properties": { + "first_used": { + "type": "string", + "format": "date-time", + "title": "First Used", + "description": "First logged occurence of spool usage." + }, + "last_used": { + "type": "string", + "format": "date-time", + "title": "Last Used", + "description": "Last logged occurence of spool usage." + }, + "filament_id": { + "type": "integer", + "title": "Filament Id", + "description": "The ID of the filament type of this spool." + }, + "remaining_weight": { + "type": "number", + "minimum": 0, + "title": "Remaining Weight", + "description": "Remaining weight of filament on the spool. Can only be used if the filament type has a weight set.", + "example": 800 + }, + "used_weight": { + "type": "number", + "minimum": 0, + "title": "Used Weight", + "description": "Used weight of filament on the spool.", + "example": 200 + }, + "location": { + "type": "string", + "maxLength": 64, + "title": "Location", + "description": "Where this spool can be found.", + "example": "Shelf A" + }, + "lot_nr": { + "type": "string", + "maxLength": 64, + "title": "Lot Nr", + "description": "Vendor manufacturing lot/batch number of the spool.", + "example": "52342" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this specific spool.", + "example": "" + }, + "archived": { + "type": "boolean", + "title": "Archived", + "description": "Whether this spool is archived and should not be used anymore.", + "default": false + } + }, + "type": "object", + "title": "SpoolUpdateParameters" + }, + "SpoolUseParameters": { + "properties": { + "use_length": { + "type": "number", + "title": "Use Length", + "description": "Length of filament to reduce by, in mm.", + "example": 2.2 + }, + "use_weight": { + "type": "number", + "title": "Use Weight", + "description": "Filament weight to reduce by, in g.", + "example": 5.3 + } + }, + "type": "object", + "title": "SpoolUseParameters" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "Vendor": { + "properties": { + "id": { + "type": "integer", + "title": "Id", + "description": "Unique internal ID of this vendor." + }, + "registered": { + "type": "string", + "format": "date-time", + "title": "Registered", + "description": "When the vendor was registered in the database. UTC Timezone." + }, + "name": { + "type": "string", + "maxLength": 64, + "title": "Name", + "description": "Vendor name.", + "example": "Polymaker" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this vendor.", + "example": "" + } + }, + "type": "object", + "required": [ + "id", + "registered", + "name" + ], + "title": "Vendor" + }, + "VendorParameters": { + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "title": "Name", + "description": "Vendor name.", + "example": "Polymaker" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this vendor.", + "example": "" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "VendorParameters" + }, + "VendorUpdateParameters": { + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "title": "Name", + "description": "Vendor name.", + "example": "Polymaker" + }, + "comment": { + "type": "string", + "maxLength": 1024, + "title": "Comment", + "description": "Free text comment about this vendor.", + "example": "" + } + }, + "type": "object", + "title": "VendorUpdateParameters" + } + } + } +} diff --git a/custom_components/spoolman/classes/spoolman_api.py b/custom_components/spoolman/classes/spoolman_api.py new file mode 100644 index 0000000..976f0a4 --- /dev/null +++ b/custom_components/spoolman/classes/spoolman_api.py @@ -0,0 +1,68 @@ +"""Class for interacting with the Spoolman API.""" +import aiohttp + + +class SpoolmanAPI: + """Class for interacting with the Spoolman API.""" + + def __init__(self, base_url, api_version="v1"): + """Initialize the Spoolman API.""" + self.base_url = f"{base_url}api/{api_version}" + + async def info(self): + """Return information about the API.""" + url = f"{self.base_url}/info" + async with aiohttp.ClientSession() as session, session.get(url) as response: + response.raise_for_status() + return await response.json() + + async def health(self): + """Return the health status of the API.""" + url = f"{self.base_url}/health" + async with aiohttp.ClientSession() as session, session.get( + url, timeout=10 + ) as response: + response.raise_for_status() + return await response.json() + + async def backup(self): + """Initiate a backup of the API.""" + url = f"{self.base_url}/backup" + async with aiohttp.ClientSession() as session, session.post(url) as response: + response.raise_for_status() + return await response.json() + + async def get_spool(self, params): + """Return a list of all spools.""" + url = f"{self.base_url}/spool" + if len(params) > 0: + url = f"{url}?{self.string_from_dictionary(params)}" + async with aiohttp.ClientSession() as session, session.get(url) as response: + response.raise_for_status() + return await response.json() + + async def get_spool_by_id(self, spool_id): + """Return the spool with the specified ID.""" + url = f"{self.base_url}/spool/{spool_id}" + async with aiohttp.ClientSession() as session, session.get(url) as response: + response.raise_for_status() + return await response.json() + + def string_from_dictionary(self, params_dict): + """Initialize an empty string to hold the result.""" + result_string = "" + + # Iterate through the dictionary and concatenate key-value pairs + for key, value in params_dict.items(): + # Convert the value to a string if it's not already + if not isinstance(value, str): + value = str(value) + + # Concatenate the key and value in the desired format + result_string += f"{key}={value} " + + # Remove the trailing space if needed + result_string = result_string.strip() + + # Return the result string + return result_string diff --git a/custom_components/spoolman/config_flow.py b/custom_components/spoolman/config_flow.py index ed073b7..9455c56 100644 --- a/custom_components/spoolman/config_flow.py +++ b/custom_components/spoolman/config_flow.py @@ -3,22 +3,22 @@ from typing import Any -import aiohttp import voluptuous as vol from homeassistant import config_entries from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import HomeAssistantError +from .classes.spoolman_api import SpoolmanAPI from .const import ( - API_HEALTH_ENDPOINT, CONF_NOTIFICATION_THRESHOLD_CRITICAL, CONF_NOTIFICATION_THRESHOLD_INFO, CONF_NOTIFICATION_THRESHOLD_WARNING, + CONF_SHOW_ARCHIVED, CONF_UPDATE_INTERVAL, CONF_URL, DOMAIN, - CONF_SHOW_ARCHIVED, NOTIFICATION_THRESHOLDS, + SPOOLMAN_INFO_PROPERTY, ) @@ -44,40 +44,22 @@ async def async_step_user( # Test the API key and URLs here if necessary # If valid, create an entry # If invalid, set errors - test_url = f"{url}{API_HEALTH_ENDPOINT}" if not errors: + spoolman_api = SpoolmanAPI(url) try: - async with aiohttp.ClientSession() as session, session.get( - test_url - ) as response: - if response.status == 200: - if "application/json" in response.headers.get( - "content-type", "" - ): - try: - data = await response.json() - if ( - isinstance(data, dict) - and data.get("status") == "healthy" - ): - return self.async_create_entry( - title=DOMAIN, - data={**user_input, CONF_URL: url}, - ) - else: - errors[ - CONF_URL - ] = "URL does not return a JSON object with a 'status' property set to 'healthy'" - except ValueError: - errors[ - CONF_URL - ] = "URL does not return valid JSON data" - else: - errors[CONF_URL] = "URL does not return JSON content" - else: - errors[ - CONF_URL - ] = f"Failed to connect to the URL. Status code: {response.status}" + healty = await spoolman_api.health() + if healty.get("status", None) == "healthy": + info = await spoolman_api.info() + + return self.async_create_entry( + title=DOMAIN, + data={ + **user_input, + CONF_URL: url, + SPOOLMAN_INFO_PROPERTY: info, + }, + ) + except Exception as error_message: errors[CONF_URL] = f"Error testing URL: {str(error_message)}" diff --git a/custom_components/spoolman/const.py b/custom_components/spoolman/const.py index 1abd10a..06212a7 100644 --- a/custom_components/spoolman/const.py +++ b/custom_components/spoolman/const.py @@ -2,6 +2,8 @@ DOMAIN = "spoolman" DEFAULT_NAME = "Spoolman" +SPOOLMAN_INFO_PROPERTY = "spoolman_info" + CONF_URL = "spoolman_url" CONF_SHOW_ARCHIVED = "show_archived" CONF_UPDATE_INTERVAL = "update-interval" @@ -9,13 +11,10 @@ CONF_NOTIFICATION_THRESHOLD_WARNING = "notification_threshold_warning" CONF_NOTIFICATION_THRESHOLD_CRITICAL = "notification_threshold_critical" -API_SPOOL_ENDPOINT = "api/v1/spool" -API_HEALTH_ENDPOINT = "api/v1/health" - PUBLIC_IMAGE_PATH = "www/spoolman_images" LOCAL_IMAGE_PATH = "/local/spoolman_images" EVENT_THRESHOLD_EXCEEDED = "threshold_exceeded" - NOTIFICATION_THRESHOLDS = {"critical": 95, "warning": 75, "info": 50} +SPOOLMAN_API_WRAPPER = "spoolman_api_wrapper" diff --git a/custom_components/spoolman/coordinator.py b/custom_components/spoolman/coordinator.py index 526eb08..53d09a7 100644 --- a/custom_components/spoolman/coordinator.py +++ b/custom_components/spoolman/coordinator.py @@ -2,16 +2,16 @@ import logging from datetime import timedelta -import aiohttp from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from .classes.spoolman_api import SpoolmanAPI from .const import ( - API_SPOOL_ENDPOINT, CONF_SHOW_ARCHIVED, CONF_UPDATE_INTERVAL, CONF_URL, DOMAIN, + SPOOLMAN_API_WRAPPER, ) _LOGGER = logging.getLogger(__name__) @@ -36,9 +36,11 @@ def __init__(self, hass: HomeAssistant, entry) -> None: ) self.my_api = url self.hass = hass + self.spoolman_api = SpoolmanAPI(entry.data[CONF_URL]) hass.data[DOMAIN] = { **entry.data, + SPOOLMAN_API_WRAPPER: self.spoolman_api, "coordinator": self, } @@ -48,12 +50,13 @@ async def _async_update_data(self): show_archived = config.get(CONF_SHOW_ARCHIVED, False) - url = f"{config[CONF_URL]}{API_SPOOL_ENDPOINT}?allow_archived={show_archived}" + try: + spools = await self.spoolman_api.get_spool( + {"allow_archived": show_archived} + ) + return spools - async with aiohttp.ClientSession() as session: - try: - async with session.get(url) as response: - data = await response.json() - return data - except Exception as e: - raise UpdateFailed(f"Error fetching data from Spoolman API: {e}") + except Exception as exception: + raise UpdateFailed( + f"Error fetching data from Spoolman API: {exception}" + ) from exception diff --git a/custom_components/spoolman/sensor.py b/custom_components/spoolman/sensor.py index 5f69846..437a64a 100644 --- a/custom_components/spoolman/sensor.py +++ b/custom_components/spoolman/sensor.py @@ -1,28 +1,29 @@ """Spoolman home assistant sensor.""" -from .const import ( - CONF_URL, - DOMAIN, - EVENT_THRESHOLD_EXCEEDED, - NOTIFICATION_THRESHOLDS, - PUBLIC_IMAGE_PATH, - LOCAL_IMAGE_PATH, -) -from .coordinator import SpoolManCoordinator - import logging import os -from PIL import Image +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.components.sensor.const import SensorStateClass from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + UnitOfMass, +) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from homeassistant.components.sensor import SensorEntity, SensorDeviceClass -from homeassistant.const import ( - UnitOfMass, +from PIL import Image + +from .const import ( + CONF_URL, + DOMAIN, + EVENT_THRESHOLD_EXCEEDED, + LOCAL_IMAGE_PATH, + NOTIFICATION_THRESHOLDS, + PUBLIC_IMAGE_PATH, + SPOOLMAN_INFO_PROPERTY, ) +from .coordinator import SpoolManCoordinator _LOGGER = logging.getLogger(__name__) @@ -54,8 +55,9 @@ def __init__(self, hass, coordinator, spool_data, idx, config_entry) -> None: """Spoolman home assistant spool sensor init.""" super().__init__(coordinator) - conf_url = hass.data[DOMAIN][CONF_URL] self.config = hass.data[DOMAIN] + conf_url = self.config[CONF_URL] + spoolman_info = self.config[SPOOLMAN_INFO_PROPERTY] self._spool = spool_data self.handled_threshold_events = [] @@ -80,6 +82,7 @@ def __init__(self, hass, coordinator, spool_data, idx, config_entry) -> None: model="Spoolman", configuration_url=conf_url, suggested_area=location_name, + sw_version=f"{spoolman_info.get('version', 'unknown')} ({spoolman_info.get('git_commit', 'unknown')})", ) self.idx = idx