diff --git a/custom_components/kobrax/manifest.json b/custom_components/kobrax/manifest.json deleted file mode 100644 index ace02a6..0000000 --- a/custom_components/kobrax/manifest.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "domain": "kobrax", - "name": "Kobra X", - "codeowners": [ - "@Gangoke" - ], - "config_flow": true, - "documentation": "https://gitea.it-drui.de/viewit/KX-Bridge-Release", - "iot_class": "local_polling", - "issue_tracker": "https://gitea.it-drui.de/viewit/KX-Bridge-Release/issues", - "version": "0.1.0" -} diff --git a/custom_components/kobrax/__init__.py b/custom_components/kobrax_lan/__init__.py similarity index 100% rename from custom_components/kobrax/__init__.py rename to custom_components/kobrax_lan/__init__.py diff --git a/custom_components/kobrax/api.py b/custom_components/kobrax_lan/api.py similarity index 90% rename from custom_components/kobrax/api.py rename to custom_components/kobrax_lan/api.py index b772525..e249892 100644 --- a/custom_components/kobrax/api.py +++ b/custom_components/kobrax_lan/api.py @@ -39,6 +39,13 @@ class KobraXApiClient: async def async_get_state(self) -> dict[str, Any]: return await self._get_json("/api/state") + async def async_get_files(self) -> list[dict[str, Any]]: + data = await self._get_json("/kx/files") + result = data.get("result", []) + if isinstance(result, list): + return result + raise KobraXApiError("Unexpected response for /kx/files") + async def async_pause_print(self) -> None: await self._post_json("/printer/print/pause", {}) diff --git a/custom_components/kobrax/binary_sensor.py b/custom_components/kobrax_lan/binary_sensor.py similarity index 85% rename from custom_components/kobrax/binary_sensor.py rename to custom_components/kobrax_lan/binary_sensor.py index 5315daf..04e133f 100644 --- a/custom_components/kobrax/binary_sensor.py +++ b/custom_components/kobrax_lan/binary_sensor.py @@ -35,7 +35,6 @@ BINARY_SENSORS: tuple[KobraXBinaryDescription, ...] = ( name="Light State", value_key="light_on", icon="mdi:lightbulb", - entity_registry_enabled_default=False, ), ) @@ -43,6 +42,10 @@ BINARY_SENSORS: tuple[KobraXBinaryDescription, ...] = ( class KobraXBinarySensor(KobraXEntity, BinarySensorEntity): entity_description: KobraXBinaryDescription + def __init__(self, coordinator, entry, description: KobraXBinaryDescription) -> None: + super().__init__(coordinator, entry, description.key, description.name) + self.entity_description = description + @property def is_on(self) -> bool: if self.entity_description.value_key == "kobra_state": @@ -56,7 +59,7 @@ async def async_setup_entry(hass, entry, async_add_entities): coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"] async_add_entities( [ - KobraXBinarySensor(coordinator, entry, description.key, description.name) + KobraXBinarySensor(coordinator, entry, description) for description in BINARY_SENSORS ] ) diff --git a/custom_components/kobrax/button.py b/custom_components/kobrax_lan/button.py similarity index 89% rename from custom_components/kobrax/button.py rename to custom_components/kobrax_lan/button.py index a0680df..ac7b7a2 100644 --- a/custom_components/kobrax/button.py +++ b/custom_components/kobrax_lan/button.py @@ -54,6 +54,10 @@ BUTTONS: tuple[KobraXButtonDescription, ...] = ( class KobraXActionButton(KobraXEntity, ButtonEntity): entity_description: KobraXButtonDescription + def __init__(self, coordinator, entry, description: KobraXButtonDescription) -> None: + super().__init__(coordinator, entry, description.key, description.name) + self.entity_description = description + async def async_press(self) -> None: api = self.hass.data[DOMAIN][self._entry.entry_id]["api"] try: @@ -76,7 +80,7 @@ async def async_setup_entry(hass, entry, async_add_entities): coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"] async_add_entities( [ - KobraXActionButton(coordinator, entry, description.key, description.name) + KobraXActionButton(coordinator, entry, description) for description in BUTTONS ] ) diff --git a/custom_components/kobrax/camera.py b/custom_components/kobrax_lan/camera.py similarity index 100% rename from custom_components/kobrax/camera.py rename to custom_components/kobrax_lan/camera.py diff --git a/custom_components/kobrax/config_flow.py b/custom_components/kobrax_lan/config_flow.py similarity index 100% rename from custom_components/kobrax/config_flow.py rename to custom_components/kobrax_lan/config_flow.py diff --git a/custom_components/kobrax/const.py b/custom_components/kobrax_lan/const.py similarity index 90% rename from custom_components/kobrax/const.py rename to custom_components/kobrax_lan/const.py index e973ec1..bc9f441 100644 --- a/custom_components/kobrax/const.py +++ b/custom_components/kobrax_lan/const.py @@ -1,7 +1,7 @@ from datetime import timedelta NAME = "Kobra X" -DOMAIN = "kobrax" +DOMAIN = "kobrax_lan" MANUFACTURER = "Anycubic" CONF_HOST = "host" @@ -19,4 +19,5 @@ PLATFORMS = [ "select", "button", "camera", + "image", ] diff --git a/custom_components/kobrax/coordinator.py b/custom_components/kobrax_lan/coordinator.py similarity index 100% rename from custom_components/kobrax/coordinator.py rename to custom_components/kobrax_lan/coordinator.py diff --git a/custom_components/kobrax/entity.py b/custom_components/kobrax_lan/entity.py similarity index 100% rename from custom_components/kobrax/entity.py rename to custom_components/kobrax_lan/entity.py diff --git a/custom_components/kobrax_lan/image.py b/custom_components/kobrax_lan/image.py new file mode 100644 index 0000000..3a319b9 --- /dev/null +++ b/custom_components/kobrax_lan/image.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +import base64 +import binascii + +from homeassistant.components.image import ImageEntity +from homeassistant.util import dt as dt_util + +from .const import DOMAIN +from .entity import KobraXEntity + + +class KobraXGCodeImage(KobraXEntity, ImageEntity): + def __init__(self, coordinator, entry, key: str, name: str) -> None: + KobraXEntity.__init__(self, coordinator, entry, key, name) + ImageEntity.__init__(self, coordinator.hass) + self._attr_content_type = "image/png" + + async def async_image(self) -> bytes | None: + api = self.hass.data[DOMAIN][self._entry.entry_id]["api"] + files = await api.async_get_files() + if not files: + return None + + active_filename = self.state_data.get("filename") + selected = None + if active_filename: + selected = next((f for f in files if f.get("filename") == active_filename), None) + if selected is None: + selected = files[0] + + thumb_b64 = selected.get("thumbnail_b64") + if not thumb_b64: + return None + + try: + image = base64.b64decode(thumb_b64, validate=True) + except (ValueError, binascii.Error): + return None + + self._attr_image_last_updated = dt_util.utcnow() + return image + + +async def async_setup_entry(hass, entry, async_add_entities): + coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"] + async_add_entities([KobraXGCodeImage(coordinator, entry, "gcode_thumbnail", "GCode Thumbnail")]) \ No newline at end of file diff --git a/custom_components/kobrax/light.py b/custom_components/kobrax_lan/light.py similarity index 100% rename from custom_components/kobrax/light.py rename to custom_components/kobrax_lan/light.py diff --git a/custom_components/kobrax_lan/manifest.json b/custom_components/kobrax_lan/manifest.json new file mode 100644 index 0000000..565d523 --- /dev/null +++ b/custom_components/kobrax_lan/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "kobrax_lan", + "name": "Kobra X LAN", + "codeowners": [ + "@Gangoke" + ], + "config_flow": true, + "documentation": "https://github.com/gangoke/kobrax-lan-hass-component", + "iot_class": "local_polling", + "issue_tracker": "https://github.com/gangoke/kobrax-lan-hass-component/issues", + "version": "0.1.0" +} diff --git a/custom_components/kobrax/select.py b/custom_components/kobrax_lan/select.py similarity index 100% rename from custom_components/kobrax/select.py rename to custom_components/kobrax_lan/select.py diff --git a/custom_components/kobrax/sensor.py b/custom_components/kobrax_lan/sensor.py similarity index 86% rename from custom_components/kobrax/sensor.py rename to custom_components/kobrax_lan/sensor.py index 1b2caea..730eaa4 100644 --- a/custom_components/kobrax/sensor.py +++ b/custom_components/kobrax_lan/sensor.py @@ -41,6 +41,7 @@ SENSORS: tuple[KobraXSensorDescription, ...] = ( value_key="nozzle_temp", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, ), KobraXSensorDescription( key="target_hotend_temp", @@ -48,7 +49,7 @@ SENSORS: tuple[KobraXSensorDescription, ...] = ( value_key="nozzle_target", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - entity_registry_enabled_default=False, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, ), KobraXSensorDescription( key="bed_temp", @@ -56,6 +57,7 @@ SENSORS: tuple[KobraXSensorDescription, ...] = ( value_key="bed_temp", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, ), KobraXSensorDescription( key="target_bed_temp", @@ -63,7 +65,7 @@ SENSORS: tuple[KobraXSensorDescription, ...] = ( value_key="bed_target", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - entity_registry_enabled_default=False, + suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, ), KobraXSensorDescription( key="filename", @@ -82,7 +84,6 @@ SENSORS: tuple[KobraXSensorDescription, ...] = ( name="Total Layers", value_key="total_layers", icon="mdi:layers", - entity_registry_enabled_default=False, ), KobraXSensorDescription( key="remaining_time", @@ -95,7 +96,6 @@ SENSORS: tuple[KobraXSensorDescription, ...] = ( name="Print Duration", value_key="print_duration", icon="mdi:timer-outline", - entity_registry_enabled_default=False, ), ) @@ -103,6 +103,10 @@ SENSORS: tuple[KobraXSensorDescription, ...] = ( class KobraXSensor(KobraXEntity, SensorEntity): entity_description: KobraXSensorDescription + def __init__(self, coordinator, entry, description: KobraXSensorDescription) -> None: + super().__init__(coordinator, entry, description.key, description.name) + self.entity_description = description + @property def native_value(self) -> Any: value = self.state_data.get(self.entity_description.value_key) @@ -115,7 +119,7 @@ async def async_setup_entry(hass, entry, async_add_entities): coordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"] async_add_entities( [ - KobraXSensor(coordinator, entry, description.key, description.name) + KobraXSensor(coordinator, entry, description) for description in SENSORS ] ) diff --git a/custom_components/kobrax/strings.json b/custom_components/kobrax_lan/strings.json similarity index 100% rename from custom_components/kobrax/strings.json rename to custom_components/kobrax_lan/strings.json diff --git a/custom_components/kobrax/translations/en.json b/custom_components/kobrax_lan/translations/en.json similarity index 100% rename from custom_components/kobrax/translations/en.json rename to custom_components/kobrax_lan/translations/en.json diff --git a/hacs.json b/hacs.json index 7b1fd38..d4672ac 100644 --- a/hacs.json +++ b/hacs.json @@ -2,7 +2,7 @@ "name": "Kobra X LAN", "content_in_root": false, "domains": [ - "kobrax" + "kobrax_lan" ], "homeassistant": "2024.6.0", "iot_class": "local_polling",