mirror of
https://github.com/gangoke/kobrax-lan-hass-component.git
synced 2026-06-09 20:52:13 +02:00
Refactor Kobra X integration into kobrax_lan component
All entites working - Moved all existing functionality from custom_components/kobrax to custom_components/kobrax_lan. - Updated manifest, const, and configuration flow to reflect the new component structure. - Reimplemented API client, coordinator, entity, and platform files under the new component. - Added support for image entities and binary sensors. - Ensured compatibility with Home Assistant's latest standards and practices.
This commit is contained in:
@@ -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"
|
||||
}
|
||||
@@ -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", {})
|
||||
|
||||
@@ -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
|
||||
]
|
||||
)
|
||||
@@ -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
|
||||
]
|
||||
)
|
||||
@@ -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",
|
||||
]
|
||||
47
custom_components/kobrax_lan/image.py
Normal file
47
custom_components/kobrax_lan/image.py
Normal file
@@ -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")])
|
||||
12
custom_components/kobrax_lan/manifest.json
Normal file
12
custom_components/kobrax_lan/manifest.json
Normal file
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
]
|
||||
)
|
||||
Reference in New Issue
Block a user