Skip to content

Commit

Permalink
Merge pull request #362 from b-rowan/reboot
Browse files Browse the repository at this point in the history
Update pyasic to use hashrate struct, and add reboot and restart.
  • Loading branch information
b-rowan authored Jun 21, 2024
2 parents c4f0b83 + caa60ee commit f7afe9d
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 39 deletions.
3 changes: 2 additions & 1 deletion custom_components/miner/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

from .const import DOMAIN, CONF_IP
from .const import CONF_IP
from .const import DOMAIN
from .coordinator import MinerCoordinator

PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.SWITCH, Platform.NUMBER]
Expand Down
29 changes: 21 additions & 8 deletions custom_components/miner/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
import pyasic
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import network
from homeassistant.core import HomeAssistant
from homeassistant.helpers.config_entry_flow import register_discovery_flow
from homeassistant.helpers.selector import TextSelector
from homeassistant.helpers.selector import TextSelectorConfig
from homeassistant.helpers.selector import TextSelectorType
from pyasic import MinerNetwork

from .const import CONF_IP
from .const import CONF_RPC_PASSWORD
Expand All @@ -19,14 +23,23 @@

_LOGGER = logging.getLogger(__name__)

# async def _async_has_devices(hass: HomeAssistant) -> bool:
# """Return if there are devices that can be discovered."""
# # TODO Check if there are any devices that can be discovered in the network.
# devices = await hass.async_add_executor_job(my_pypi_dependency.discover)
# return len(devices) > 0

async def _async_has_devices(hass: HomeAssistant) -> bool:
"""Return if there are devices that can be discovered."""
adapters = await network.async_get_adapters(hass)

# config_entry_flow.register_discovery_flow(DOMAIN, "miner", _async_has_devices)
for adapter in adapters:
for ip_info in adapter["ipv4"]:
local_ip = ip_info["address"]
network_prefix = ip_info["network_prefix"]
miner_net = MinerNetwork.from_subnet(f"{local_ip}/{network_prefix}")
miners = await miner_net.scan()
if len(miners) > 0:
return True
return False


register_discovery_flow(DOMAIN, "miner", _async_has_devices)


async def validate_ip_input(
Expand Down Expand Up @@ -82,8 +95,8 @@ async def async_step_login(self, user_input=None):

schema_data = {}

if self._miner.api is not None:
if self._miner.api.pwd is not None:
if self._miner.rpc is not None:
if self._miner.rpc.pwd is not None:
schema_data[
vol.Optional(
CONF_RPC_PASSWORD,
Expand Down
3 changes: 3 additions & 0 deletions custom_components/miner/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
CONF_WEB_PASSWORD = "web_password"
CONF_WEB_USERNAME = "web_username"

SERVICE_REBOOT = "reboot"
SERVICE_RESTART_BACKEND = "restart_backend"

TERA_HASH_PER_SECOND = "TH/s"
JOULES_PER_TERA_HASH = "J/TH"
25 changes: 12 additions & 13 deletions custom_components/miner/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.helpers.update_coordinator import UpdateFailed

from .const import (
CONF_IP,
CONF_RPC_PASSWORD,
CONF_SSH_PASSWORD,
CONF_SSH_USERNAME,
CONF_WEB_PASSWORD,
CONF_WEB_USERNAME,
)
from .const import CONF_IP
from .const import CONF_RPC_PASSWORD
from .const import CONF_SSH_PASSWORD
from .const import CONF_SSH_USERNAME
from .const import CONF_WEB_PASSWORD
from .const import CONF_WEB_USERNAME

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -58,7 +57,7 @@ async def _async_update_data(self):
if self.miner is None:
raise UpdateFailed("Miner Offline")

_LOGGER.debug(f"Found miner :{self.miner}")
_LOGGER.debug(f"Found miner: {self.miner}")

try:
if self.miner.api is not None:
Expand Down Expand Up @@ -115,8 +114,8 @@ async def _async_update_data(self):
"is_mining": miner_data.is_mining,
"fw_ver": miner_data.fw_ver,
"miner_sensors": {
"hashrate": hashrate,
"ideal_hashrate": expected_hashrate,
"hashrate": round(float(miner_data.hashrate or 0), 2),
"ideal_hashrate": round(float(miner_data.expected_hashrate or 0), 2),
"temperature": miner_data.temperature_avg,
"power_limit": miner_data.wattage_limit,
"miner_consumption": miner_data.wattage,
Expand All @@ -126,7 +125,7 @@ async def _async_update_data(self):
board.slot: {
"board_temperature": board.temp,
"chip_temperature": board.chip_temp,
"board_hashrate": board.hashrate,
"board_hashrate": round(float(board.hashrate or 0), 2),
}
for board in miner_data.hashboards
},
Expand Down
84 changes: 84 additions & 0 deletions custom_components/miner/device_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""Provides device actions for Miner."""
from __future__ import annotations

import logging

import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.const import CONF_DEVICE_ID
from homeassistant.const import CONF_DOMAIN
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.const import CONF_TYPE
from homeassistant.core import Context
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import entity_registry as er

from .const import DOMAIN
from .const import SERVICE_REBOOT
from .const import SERVICE_RESTART_BACKEND

_LOGGER = logging.getLogger(__name__)

ACTION_TYPES = {"reboot", "restart_backend"}

ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
{
vol.Required(CONF_TYPE): vol.In(ACTION_TYPES),
vol.Required(CONF_ENTITY_ID): cv.entity_domain(DOMAIN),
}
)


async def async_get_actions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device actions for Miner devices."""
registry = er.async_get(hass)
actions = []

# Get all the integrations entities for this device
for entry in er.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue

# Add actions for each entity that belongs to this integration
base_action = {
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
}
for action_type in ACTION_TYPES:
try:
actions.append(
{
**base_action,
CONF_TYPE: action_type,
}
)
except AttributeError:
_LOGGER.error(
"Failed to run device command for miner: Unable to access entry data."
)

return actions


async def async_call_action_from_config(
hass: HomeAssistant, config: dict, variables: dict, context: Context | None
) -> None:
"""Execute a device action."""
service = None
service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]}

if config[CONF_TYPE] == "reboot":
service = SERVICE_REBOOT
elif config[CONF_TYPE] == "restart_backend":
service = SERVICE_RESTART_BACKEND

if service is None:
_LOGGER.error(f"Failed to call the service {config[CONF_TYPE]} for miner.")
return
await hass.services.async_call(
DOMAIN, service, service_data, blocking=True, context=context
)
2 changes: 1 addition & 1 deletion custom_components/miner/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"homekit": {},
"iot_class": "local_polling",
"issue_tracker": "https://github.com/Schnitzel/hass-miner/issues",
"requirements": ["pyasic==0.57.4"],
"requirements": ["pyasic==0.57.6"],
"ssdp": [],
"version": "1.1.9",
"zeroconf": []
Expand Down
15 changes: 1 addition & 14 deletions custom_components/miner/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,6 @@ async def async_setup_entry(
]
)

# @callback
# def new_data_received():
# """Check for new sensors."""
# entities = [
# _create_entity(key) for key in coordinator.data if key not in created
# ]
# if entities:
# async_add_entities(entities)

# coordinator.async_add_listener(new_data_received)


class MinerPowerLimitNumber(CoordinatorEntity[MinerCoordinator], NumberEntity):
"""Defines a Miner Number to set the Power Limit of the Miner."""
Expand Down Expand Up @@ -107,9 +96,7 @@ async def async_set_native_value(self, value):
)

if not miner.supports_autotuning:
raise TypeError(
f"{self.coordinator.entry.title}: Tuning not supported."
)
raise TypeError(f"{self.coordinator.entry.title}: Tuning not supported.")

result = await miner.set_power_limit(int(value))

Expand Down
35 changes: 35 additions & 0 deletions custom_components/miner/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""The Miner component services."""
from __future__ import annotations

import logging

import pyasic
from homeassistant.core import HomeAssistant
from homeassistant.core import ServiceCall

from .const import CONF_IP
from .const import DOMAIN
from .const import SERVICE_REBOOT
from .const import SERVICE_RESTART_BACKEND

LOGGER = logging.getLogger(__name__)


async def async_setup_services(hass: HomeAssistant) -> None:
"""Service handler setup."""

async def reboot(call: ServiceCall) -> None:
ip = call.data.get(CONF_IP)

miner = await pyasic.get_miner(ip)
await miner.reboot()

hass.services.async_register(DOMAIN, SERVICE_REBOOT, reboot)

async def restart_backend(call: ServiceCall) -> None:
ip = call.data.get(CONF_IP)

miner = await pyasic.get_miner(ip)
await miner.restart_backend()

hass.services.async_register(DOMAIN, SERVICE_RESTART_BACKEND, restart_backend)
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
colorlog==6.7.0
homeassistant>=2024.1.0
homeassistant>=2024.6.3
pip>=21.0,<23.2
ruff==0.0.267
pyasic==0.54.17
pyasic==0.57.6
setuptools==69.0.3
pre-commit

0 comments on commit f7afe9d

Please sign in to comment.