mirror of
https://github.com/anxms/fn_nas.git
synced 2025-10-15 17:48:29 +00:00
新增docker容器控制功能
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from .const import DOMAIN, DATA_UPDATE_COORDINATOR, PLATFORMS
|
from .const import DOMAIN, DATA_UPDATE_COORDINATOR, PLATFORMS, CONF_ENABLE_DOCKER # 导入新增常量
|
||||||
from .coordinator import FlynasCoordinator, UPSDataUpdateCoordinator
|
from .coordinator import FlynasCoordinator, UPSDataUpdateCoordinator
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -16,13 +16,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
_LOGGER.debug("协调器是否有control_vm方法: %s", hasattr(coordinator, 'control_vm'))
|
_LOGGER.debug("协调器是否有control_vm方法: %s", hasattr(coordinator, 'control_vm'))
|
||||||
_LOGGER.debug("协调器是否有vm_manager属性: %s", hasattr(coordinator, 'vm_manager'))
|
_LOGGER.debug("协调器是否有vm_manager属性: %s", hasattr(coordinator, 'vm_manager'))
|
||||||
|
|
||||||
|
# 检查是否启用Docker,并初始化Docker管理器(如果有)
|
||||||
|
enable_docker = config.get(CONF_ENABLE_DOCKER, False)
|
||||||
|
if enable_docker:
|
||||||
|
# 导入Docker管理器并初始化
|
||||||
|
from .docker_manager import DockerManager
|
||||||
|
coordinator.docker_manager = DockerManager(coordinator)
|
||||||
|
_LOGGER.debug("已启用Docker容器监控")
|
||||||
|
else:
|
||||||
|
coordinator.docker_manager = None
|
||||||
|
_LOGGER.debug("未启用Docker容器监控")
|
||||||
|
|
||||||
ups_coordinator = UPSDataUpdateCoordinator(hass, config, coordinator)
|
ups_coordinator = UPSDataUpdateCoordinator(hass, config, coordinator)
|
||||||
await ups_coordinator.async_config_entry_first_refresh()
|
await ups_coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
hass.data[DOMAIN][entry.entry_id] = {
|
||||||
DATA_UPDATE_COORDINATOR: coordinator,
|
DATA_UPDATE_COORDINATOR: coordinator,
|
||||||
"ups_coordinator": ups_coordinator
|
"ups_coordinator": ups_coordinator,
|
||||||
|
CONF_ENABLE_DOCKER: enable_docker # 存储启用状态
|
||||||
}
|
}
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
@@ -2,13 +2,16 @@ import logging
|
|||||||
from homeassistant.components.button import ButtonEntity
|
from homeassistant.components.button import ButtonEntity
|
||||||
from homeassistant.helpers.entity import EntityCategory
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
from .const import DOMAIN, DATA_UPDATE_COORDINATOR, DEVICE_ID_NAS
|
from .const import (
|
||||||
|
DOMAIN, DATA_UPDATE_COORDINATOR, DEVICE_ID_NAS, CONF_ENABLE_DOCKER
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
domain_data = hass.data[DOMAIN][config_entry.entry_id]
|
domain_data = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
coordinator = domain_data[DATA_UPDATE_COORDINATOR]
|
coordinator = domain_data[DATA_UPDATE_COORDINATOR]
|
||||||
|
enable_docker = domain_data.get(CONF_ENABLE_DOCKER, False)
|
||||||
|
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
@@ -23,7 +26,21 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
coordinator,
|
coordinator,
|
||||||
vm["name"],
|
vm["name"],
|
||||||
vm.get("title", vm["name"]),
|
vm.get("title", vm["name"]),
|
||||||
config_entry.entry_id # 传递entry_id用于生成唯一ID
|
config_entry.entry_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. 添加Docker容器重启按钮(如果启用了Docker功能)
|
||||||
|
if enable_docker and "docker_containers" in coordinator.data:
|
||||||
|
for container in coordinator.data["docker_containers"]:
|
||||||
|
# 使用容器名称生成安全ID(替换特殊字符)
|
||||||
|
safe_name = container["name"].replace(" ", "_").replace("/", "_").replace(".", "_")
|
||||||
|
entities.append(
|
||||||
|
DockerContainerRestartButton(
|
||||||
|
coordinator,
|
||||||
|
container["name"],
|
||||||
|
safe_name,
|
||||||
|
config_entry.entry_id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +50,7 @@ class RebootButton(CoordinatorEntity, ButtonEntity):
|
|||||||
def __init__(self, coordinator, entry_id):
|
def __init__(self, coordinator, entry_id):
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self._attr_name = "重启"
|
self._attr_name = "重启"
|
||||||
self._attr_unique_id = f"{entry_id}_flynas_reboot" # 使用entry_id确保唯一性
|
self._attr_unique_id = f"{entry_id}_flynas_reboot"
|
||||||
self._attr_entity_category = EntityCategory.CONFIG
|
self._attr_entity_category = EntityCategory.CONFIG
|
||||||
self._attr_device_info = {
|
self._attr_device_info = {
|
||||||
"identifiers": {(DOMAIN, DEVICE_ID_NAS)},
|
"identifiers": {(DOMAIN, DEVICE_ID_NAS)},
|
||||||
@@ -58,7 +75,7 @@ class VMRebootButton(CoordinatorEntity, ButtonEntity):
|
|||||||
self.vm_name = vm_name
|
self.vm_name = vm_name
|
||||||
self.vm_title = vm_title
|
self.vm_title = vm_title
|
||||||
self._attr_name = f"{vm_title} 重启"
|
self._attr_name = f"{vm_title} 重启"
|
||||||
self._attr_unique_id = f"{entry_id}_flynas_vm_{vm_name}_reboot" # 使用entry_id确保唯一性
|
self._attr_unique_id = f"{entry_id}_flynas_vm_{vm_name}_reboot"
|
||||||
self._attr_device_info = {
|
self._attr_device_info = {
|
||||||
"identifiers": {(DOMAIN, f"vm_{vm_name}")},
|
"identifiers": {(DOMAIN, f"vm_{vm_name}")},
|
||||||
"name": vm_title,
|
"name": vm_title,
|
||||||
@@ -86,3 +103,63 @@ class VMRebootButton(CoordinatorEntity, ButtonEntity):
|
|||||||
self.coordinator.async_add_listener(self.async_write_ha_state)
|
self.coordinator.async_add_listener(self.async_write_ha_state)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_LOGGER.error("重启虚拟机时出错: %s", str(e), exc_info=True)
|
_LOGGER.error("重启虚拟机时出错: %s", str(e), exc_info=True)
|
||||||
|
|
||||||
|
class DockerContainerRestartButton(CoordinatorEntity, ButtonEntity):
|
||||||
|
def __init__(self, coordinator, container_name, safe_name, entry_id):
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self.container_name = container_name
|
||||||
|
self.safe_name = safe_name
|
||||||
|
self._attr_name = f"{container_name} 重启"
|
||||||
|
self._attr_unique_id = f"{entry_id}_docker_{safe_name}_restart"
|
||||||
|
self._attr_device_info = {
|
||||||
|
"identifiers": {(DOMAIN, f"docker_{safe_name}")},
|
||||||
|
"name": container_name,
|
||||||
|
"via_device": (DOMAIN, DEVICE_ID_NAS)
|
||||||
|
}
|
||||||
|
self._attr_icon = "mdi:docker"
|
||||||
|
|
||||||
|
async def async_press(self):
|
||||||
|
"""重启Docker容器"""
|
||||||
|
# 检查是否启用了Docker功能
|
||||||
|
if not hasattr(self.coordinator, 'docker_manager') or self.coordinator.docker_manager is None:
|
||||||
|
_LOGGER.error("Docker管理功能未启用,无法重启容器 %s", self.container_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 更新状态为"重启中"
|
||||||
|
for container in self.coordinator.data.get("docker_containers", []):
|
||||||
|
if container["name"] == self.container_name:
|
||||||
|
container["status"] = "restarting"
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# 执行重启命令
|
||||||
|
success = await self.coordinator.docker_manager.control_container(self.container_name, "restart")
|
||||||
|
|
||||||
|
if success:
|
||||||
|
_LOGGER.info("Docker容器 %s 重启命令已发送", self.container_name)
|
||||||
|
|
||||||
|
# 强制刷新状态(因为容器重启可能需要时间)
|
||||||
|
self.coordinator.async_request_refresh()
|
||||||
|
else:
|
||||||
|
_LOGGER.error("Docker容器 %s 重启失败", self.container_name)
|
||||||
|
# 恢复原始状态
|
||||||
|
for container in self.coordinator.data.get("docker_containers", []):
|
||||||
|
if container["name"] == self.container_name:
|
||||||
|
container["status"] = "running" # 假设重启失败后状态不变
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
_LOGGER.error("重启Docker容器 %s 时出错: %s", self.container_name, str(e), exc_info=True)
|
||||||
|
# 恢复原始状态
|
||||||
|
for container in self.coordinator.data.get("docker_containers", []):
|
||||||
|
if container["name"] == self.container_name:
|
||||||
|
container["status"] = "running"
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra_state_attributes(self):
|
||||||
|
return {
|
||||||
|
"容器名称": self.container_name,
|
||||||
|
"操作类型": "重启容器",
|
||||||
|
"提示": "重启操作可能需要一些时间完成"
|
||||||
|
}
|
@@ -17,7 +17,8 @@ from .const import (
|
|||||||
CONF_FAN_CONFIG_PATH,
|
CONF_FAN_CONFIG_PATH,
|
||||||
CONF_UPS_SCAN_INTERVAL,
|
CONF_UPS_SCAN_INTERVAL,
|
||||||
DEFAULT_UPS_SCAN_INTERVAL,
|
DEFAULT_UPS_SCAN_INTERVAL,
|
||||||
CONF_ROOT_PASSWORD
|
CONF_ROOT_PASSWORD,
|
||||||
|
CONF_ENABLE_DOCKER
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -68,7 +69,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_SCAN_INTERVAL,
|
CONF_SCAN_INTERVAL,
|
||||||
default=DEFAULT_SCAN_INTERVAL
|
default=DEFAULT_SCAN_INTERVAL
|
||||||
): int
|
): int,
|
||||||
|
# 添加启用Docker的选项
|
||||||
|
vol.Optional(CONF_ENABLE_DOCKER, default=False): bool
|
||||||
})
|
})
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@@ -96,7 +99,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
selected_mac = user_input.get(CONF_MAC)
|
selected_mac = user_input.get(CONF_MAC)
|
||||||
if selected_mac:
|
if selected_mac:
|
||||||
|
# 将CONF_ENABLE_DOCKER从ssh_config复制到最终配置
|
||||||
|
enable_docker = self.ssh_config.get(CONF_ENABLE_DOCKER, False)
|
||||||
self.ssh_config[CONF_MAC] = selected_mac
|
self.ssh_config[CONF_MAC] = selected_mac
|
||||||
|
# 确保将CONF_ENABLE_DOCKER也存入配置项
|
||||||
|
self.ssh_config[CONF_ENABLE_DOCKER] = enable_docker
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=self.ssh_config[CONF_HOST],
|
title=self.ssh_config[CONF_HOST],
|
||||||
data=self.ssh_config
|
data=self.ssh_config
|
||||||
@@ -208,7 +215,12 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_UPS_SCAN_INTERVAL,
|
CONF_UPS_SCAN_INTERVAL,
|
||||||
default=data.get(CONF_UPS_SCAN_INTERVAL, DEFAULT_UPS_SCAN_INTERVAL)
|
default=data.get(CONF_UPS_SCAN_INTERVAL, DEFAULT_UPS_SCAN_INTERVAL)
|
||||||
): int
|
): int,
|
||||||
|
# 在选项流程中也添加启用Docker的选项
|
||||||
|
vol.Optional(
|
||||||
|
CONF_ENABLE_DOCKER,
|
||||||
|
default=data.get(CONF_ENABLE_DOCKER, False)
|
||||||
|
): bool
|
||||||
})
|
})
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
|
@@ -18,6 +18,8 @@ CONF_FAN_CONFIG_PATH = "fan_config_path"
|
|||||||
CONF_IGNORE_DISKS = "ignore_disks"
|
CONF_IGNORE_DISKS = "ignore_disks"
|
||||||
CONF_MAC = "mac"
|
CONF_MAC = "mac"
|
||||||
CONF_UPS_SCAN_INTERVAL = "ups_scan_interval"
|
CONF_UPS_SCAN_INTERVAL = "ups_scan_interval"
|
||||||
|
CONF_ENABLE_DOCKER = "enable_docker"
|
||||||
|
DOCKER_CONTAINERS = "docker_containers"
|
||||||
|
|
||||||
DEFAULT_PORT = 22
|
DEFAULT_PORT = 22
|
||||||
DEFAULT_SCAN_INTERVAL = 60
|
DEFAULT_SCAN_INTERVAL = 60
|
||||||
|
@@ -9,12 +9,13 @@ from .const import (
|
|||||||
DOMAIN, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD,
|
DOMAIN, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD,
|
||||||
CONF_IGNORE_DISKS, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
|
CONF_IGNORE_DISKS, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
|
||||||
DEFAULT_PORT, CONF_MAC, CONF_UPS_SCAN_INTERVAL, DEFAULT_UPS_SCAN_INTERVAL,
|
DEFAULT_PORT, CONF_MAC, CONF_UPS_SCAN_INTERVAL, DEFAULT_UPS_SCAN_INTERVAL,
|
||||||
CONF_ROOT_PASSWORD
|
CONF_ROOT_PASSWORD, CONF_ENABLE_DOCKER
|
||||||
)
|
)
|
||||||
from .disk_manager import DiskManager
|
from .disk_manager import DiskManager
|
||||||
from .system_manager import SystemManager
|
from .system_manager import SystemManager
|
||||||
from .ups_manager import UPSManager
|
from .ups_manager import UPSManager
|
||||||
from .vm_manager import VMManager
|
from .vm_manager import VMManager
|
||||||
|
from .docker_manager import DockerManager
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -27,6 +28,8 @@ class FlynasCoordinator(DataUpdateCoordinator):
|
|||||||
self.password = config[CONF_PASSWORD]
|
self.password = config[CONF_PASSWORD]
|
||||||
self.root_password = config.get(CONF_ROOT_PASSWORD)
|
self.root_password = config.get(CONF_ROOT_PASSWORD)
|
||||||
self.mac = config.get(CONF_MAC, "")
|
self.mac = config.get(CONF_MAC, "")
|
||||||
|
self.enable_docker = config.get(CONF_ENABLE_DOCKER, False)
|
||||||
|
self.docker_manager = DockerManager(self) if self.enable_docker else None
|
||||||
self.ssh = None
|
self.ssh = None
|
||||||
self.ssh_closed = True
|
self.ssh_closed = True
|
||||||
self.ups_manager = UPSManager(self)
|
self.ups_manager = UPSManager(self)
|
||||||
@@ -236,6 +239,10 @@ class FlynasCoordinator(DataUpdateCoordinator):
|
|||||||
for vm in vms:
|
for vm in vms:
|
||||||
vm["title"] = await self.vm_manager.get_vm_title(vm["name"])
|
vm["title"] = await self.vm_manager.get_vm_title(vm["name"])
|
||||||
|
|
||||||
|
docker_containers = []
|
||||||
|
if self.enable_docker:
|
||||||
|
docker_containers = await self.docker_manager.get_containers()
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"disks": disks,
|
"disks": disks,
|
||||||
"system": {
|
"system": {
|
||||||
@@ -243,7 +250,8 @@ class FlynasCoordinator(DataUpdateCoordinator):
|
|||||||
"status": status
|
"status": status
|
||||||
},
|
},
|
||||||
"ups": ups_info,
|
"ups": ups_info,
|
||||||
"vms": vms
|
"vms": vms,
|
||||||
|
"docker_containers": docker_containers
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
55
custom_components/fn_nas/docker_manager.py
Normal file
55
custom_components/fn_nas/docker_manager.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class DockerManager:
|
||||||
|
def __init__(self, coordinator):
|
||||||
|
self.coordinator = coordinator
|
||||||
|
self.logger = _LOGGER.getChild("docker_manager")
|
||||||
|
self.logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
async def get_containers(self) -> List[Dict[str, str]]:
|
||||||
|
"""获取Docker容器列表及其状态"""
|
||||||
|
try:
|
||||||
|
# 使用docker命令获取容器列表,格式为JSON
|
||||||
|
output = await self.coordinator.run_command("docker ps -a --format '{{json .}}'")
|
||||||
|
self.logger.debug("Docker容器原始输出: %s", output)
|
||||||
|
containers = []
|
||||||
|
|
||||||
|
# 每行一个容器的JSON
|
||||||
|
for line in output.splitlines():
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
# 解析JSON
|
||||||
|
container_info = json.loads(line)
|
||||||
|
# 提取所需字段
|
||||||
|
container = {
|
||||||
|
"id": container_info.get("ID", ""),
|
||||||
|
"name": container_info.get("Names", ""),
|
||||||
|
"status": container_info.get("State", "").lower(),
|
||||||
|
"image": container_info.get("Image", ""),
|
||||||
|
}
|
||||||
|
containers.append(container)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
self.logger.warning("解析Docker容器信息失败: %s", line)
|
||||||
|
return containers
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error("获取Docker容器列表失败: %s", str(e), exc_info=True)
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def control_container(self, container_name, action):
|
||||||
|
"""控制容器操作"""
|
||||||
|
valid_actions = ["start", "stop", "restart"]
|
||||||
|
if action not in valid_actions:
|
||||||
|
raise ValueError(f"无效操作: {action}")
|
||||||
|
|
||||||
|
command = f"docker {action} {container_name}"
|
||||||
|
try:
|
||||||
|
await self.coordinator.run_command(command)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error("执行Docker容器操作失败: %s", str(e), exc_info=True)
|
||||||
|
return False
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "fn_nas",
|
"domain": "fn_nas",
|
||||||
"name": "飞牛NAS",
|
"name": "飞牛NAS",
|
||||||
"version": "1.2.4",
|
"version": "1.3.0",
|
||||||
"documentation": "https://github.com/anxms/fn_nas",
|
"documentation": "https://github.com/anxms/fn_nas",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@anxms"],
|
"codeowners": ["@anxms"],
|
||||||
|
@@ -229,6 +229,21 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
)
|
)
|
||||||
existing_ids.add(ups_status_uid)
|
existing_ids.add(ups_status_uid)
|
||||||
|
|
||||||
|
if coordinator.data.get("docker_containers") and coordinator.enable_docker:
|
||||||
|
for container in coordinator.data["docker_containers"]:
|
||||||
|
safe_name = container["name"].replace(" ", "_").replace("/", "_")
|
||||||
|
sensor_uid = f"{config_entry.entry_id}_docker_{safe_name}_status"
|
||||||
|
if sensor_uid not in existing_ids:
|
||||||
|
entities.append(
|
||||||
|
DockerContainerStatusSensor(
|
||||||
|
coordinator,
|
||||||
|
container["name"],
|
||||||
|
safe_name,
|
||||||
|
config_entry.entry_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
existing_ids.add(sensor_uid)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
@@ -495,3 +510,31 @@ class VMStatusSensor(CoordinatorEntity, SensorEntity):
|
|||||||
elif vm["state"] == "rebooting":
|
elif vm["state"] == "rebooting":
|
||||||
return "mdi:server-security"
|
return "mdi:server-security"
|
||||||
return "mdi:server"
|
return "mdi:server"
|
||||||
|
|
||||||
|
# 添加DockerContainerStatusSensor类
|
||||||
|
class DockerContainerStatusSensor(CoordinatorEntity, SensorEntity):
|
||||||
|
def __init__(self, coordinator, container_name, safe_name, entry_id):
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self.container_name = container_name
|
||||||
|
self._attr_name = f"{container_name} 状态"
|
||||||
|
self._attr_unique_id = f"{entry_id}_docker_{safe_name}_status"
|
||||||
|
self._attr_device_info = {
|
||||||
|
"identifiers": {(DOMAIN, f"docker_{safe_name}")},
|
||||||
|
"name": container_name,
|
||||||
|
"via_device": (DOMAIN, DEVICE_ID_NAS)
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_value(self):
|
||||||
|
for container in self.coordinator.data.get("docker_containers", []):
|
||||||
|
if container["name"] == self.container_name:
|
||||||
|
# 状态映射为中文
|
||||||
|
status_map = {
|
||||||
|
"running": "运行中",
|
||||||
|
"exited": "已停止",
|
||||||
|
"paused": "已暂停",
|
||||||
|
"restarting": "重启中",
|
||||||
|
"dead": "死亡"
|
||||||
|
}
|
||||||
|
return status_map.get(container["status"], container["status"])
|
||||||
|
return "未知"
|
@@ -24,6 +24,18 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if coordinator.data.get("docker_containers") and coordinator.enable_docker:
|
||||||
|
for container in coordinator.data["docker_containers"]:
|
||||||
|
# 使用容器名称作为唯一ID的一部分
|
||||||
|
safe_name = container["name"].replace(" ", "_").replace("/", "_")
|
||||||
|
entities.append(
|
||||||
|
DockerContainerSwitch(
|
||||||
|
coordinator,
|
||||||
|
container["name"],
|
||||||
|
safe_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
class PowerSwitch(CoordinatorEntity, SwitchEntity):
|
class PowerSwitch(CoordinatorEntity, SwitchEntity):
|
||||||
@@ -147,3 +159,42 @@ class VMSwitch(CoordinatorEntity, SwitchEntity):
|
|||||||
"原始状态": vm["state"]
|
"原始状态": vm["state"]
|
||||||
}
|
}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# 添加DockerContainerSwitch类
|
||||||
|
class DockerContainerSwitch(CoordinatorEntity, SwitchEntity):
|
||||||
|
def __init__(self, coordinator, container_name, safe_name):
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self.container_name = container_name
|
||||||
|
self._attr_name = f"{container_name} 容器"
|
||||||
|
self._attr_unique_id = f"docker_{safe_name}_switch"
|
||||||
|
self._attr_device_info = {
|
||||||
|
"identifiers": {(DOMAIN, f"docker_{safe_name}")},
|
||||||
|
"name": container_name,
|
||||||
|
"via_device": (DOMAIN, DEVICE_ID_NAS)
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
for container in self.coordinator.data.get("docker_containers", []):
|
||||||
|
if container["name"] == self.container_name:
|
||||||
|
return container["status"] == "running"
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs):
|
||||||
|
if self.coordinator.docker_manager:
|
||||||
|
success = await self.coordinator.docker_manager.control_container(self.container_name, "start")
|
||||||
|
if success:
|
||||||
|
# 更新状态
|
||||||
|
for container in self.coordinator.data.get("docker_containers", []):
|
||||||
|
if container["name"] == self.container_name:
|
||||||
|
container["status"] = "running"
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs):
|
||||||
|
if self.coordinator.docker_manager:
|
||||||
|
success = await self.coordinator.docker_manager.control_container(self.container_name, "stop")
|
||||||
|
if success:
|
||||||
|
for container in self.coordinator.data.get("docker_containers", []):
|
||||||
|
if container["name"] == self.container_name:
|
||||||
|
container["status"] = "exited" # Docker停止后状态为exited
|
||||||
|
self.async_write_ha_state()
|
@@ -9,7 +9,8 @@
|
|||||||
"port": "端口",
|
"port": "端口",
|
||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
"password": "密码",
|
"password": "密码",
|
||||||
"scan_interval": "数据更新间隔(秒)"
|
"scan_interval": "数据更新间隔(秒)",
|
||||||
|
"enable_docker": "启用docker控制"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"select_mac": {
|
"select_mac": {
|
||||||
|
Reference in New Issue
Block a user