Skip to content

Commit

Permalink
Merge pull request #292 from b-rowan/main
Browse files Browse the repository at this point in the history
Update pyasic version, and attempt to fix duplicate entities
  • Loading branch information
b-rowan authored Jan 23, 2024
2 parents 3e8fe0c + 5083d0d commit 3fe0438
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 275 deletions.
12 changes: 9 additions & 3 deletions custom_components/miner/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
"""The Miner integration."""
from __future__ import annotations

import pyasic
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

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

# TODO List the platforms that you want to support.
# For your initial PR, limit it to 1 platform.
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.SWITCH, Platform.NUMBER]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Miner from a config entry."""

miner_ip = entry.data[CONF_IP]
miner = await pyasic.get_miner(miner_ip)

if miner is None:
raise ConfigEntryNotReady("Miner could not be found.")

m_coordinator = MinerCoordinator(hass, entry)
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = m_coordinator

Expand Down
161 changes: 109 additions & 52 deletions custom_components/miner/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@

import pyasic
import voluptuous as vol
from homeassistant import config_entries, exceptions
from homeassistant.helpers.selector import (
TextSelector,
TextSelectorConfig,
TextSelectorType,
)

from .const import CONF_IP, CONF_PASSWORD, CONF_TITLE, CONF_USERNAME, DOMAIN
from homeassistant import config_entries
from homeassistant.helpers.selector import TextSelector
from homeassistant.helpers.selector import TextSelectorConfig
from homeassistant.helpers.selector import TextSelectorType

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_TITLE
from .const import CONF_WEB_PASSWORD
from .const import CONF_WEB_USERNAME
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

Expand All @@ -21,24 +26,20 @@
# return len(devices) > 0


# config_entry_flow.register_discovery_flow(DOMAIN, "Miner", _async_has_devices)
# config_entry_flow.register_discovery_flow(DOMAIN, "miner", _async_has_devices)


async def validate_input(data: dict[str, str]) -> dict[str, str]:
async def validate_ip_input(
data: dict[str, str]
) -> tuple[dict[str, str], pyasic.AnyMiner | None]:
"""Validate the user input allows us to connect."""
miner_ip = data.get(CONF_IP)
miner_username = data.get(CONF_USERNAME)
miner_password = data.get(CONF_PASSWORD)

miner = await pyasic.get_miner(miner_ip)
if miner is None:
return {"base": "Unable to connect to Miner, is IP correct?"}

miner.username = miner_username
miner.pwd = miner_password
await miner.get_data(include=["mac"])
return {"base": "Unable to connect to Miner, is IP correct?"}, None

return {}
return {}, miner


class MinerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
Expand All @@ -49,44 +50,108 @@ class MinerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self):
"""Initialize."""
self._data = {}
self._miner = None

async def async_step_user(self, user_input=None):
"""Handle the initial step."""
"""Get miner IP and check if it is available."""
if user_input is None:
user_input = {}

schema = vol.Schema(
{
vol.Required(CONF_IP, default=user_input.get(CONF_IP, "")): str,
vol.Required(
CONF_USERNAME, default=user_input.get(CONF_USERNAME, "root")
): str,
vol.Optional(
CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "")
): TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD, autocomplete="current-password"
)
),
}
{vol.Required(CONF_IP, default=user_input.get(CONF_IP, "")): str}
)

if not user_input:
return self.async_show_form(step_id="user", data_schema=schema)

errors = await validate_input(user_input)
errors, miner = await validate_ip_input(user_input)

if not errors:
self._data.update(user_input)
return await self.async_step_title()
if errors:
return self.async_show_form(
step_id="user", data_schema=schema, errors=errors
)

return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
self._miner = miner
self._data.update(user_input)
return await self.async_step_login()

async def async_step_title(self, user_input=None):
"""Ask for Entity Title."""
async def async_step_login(self, user_input=None):
"""Get miner login credentials."""
if user_input is None:
user_input = {}

miner_ip = self._data.get(CONF_IP)
miner = await pyasic.get_miner(miner_ip) # should be fast, cached
title = await miner.get_hostname()
schema_data = {}

if self._miner.api is not None:
if self._miner.api.pwd is not None:
schema_data[
vol.Optional(
CONF_RPC_PASSWORD,
default=user_input.get(
CONF_RPC_PASSWORD,
self._miner.web.pwd
if self._miner.api.pwd is not None
else "",
),
)
] = TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD, autocomplete="current-password"
)
)

if self._miner.web is not None:
schema_data[
vol.Required(
CONF_WEB_USERNAME,
default=user_input.get(CONF_WEB_USERNAME, self._miner.web.username),
)
] = str
schema_data[
vol.Optional(
CONF_WEB_PASSWORD,
default=user_input.get(
CONF_WEB_PASSWORD,
self._miner.web.pwd if self._miner.web.pwd is not None else "",
),
)
] = TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD, autocomplete="current-password"
)
)

if self._miner.ssh is not None:
schema_data[
vol.Required(
CONF_SSH_USERNAME,
default=user_input.get(CONF_SSH_USERNAME, self._miner.ssh.username),
)
] = str
schema_data[
vol.Optional(
CONF_SSH_PASSWORD,
default=user_input.get(
CONF_SSH_PASSWORD,
self._miner.ssh.pwd if self._miner.ssh.pwd is not None else "",
),
)
] = TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD, autocomplete="current-password"
)
)

schema = vol.Schema(schema_data)
if not user_input:
return self.async_show_form(step_id="login", data_schema=schema)

self._data.update(user_input)
return await self.async_step_title()

async def async_step_title(self, user_input=None):
"""Get entity title."""
title = await self._miner.get_hostname()

if user_input is None:
user_input = {}
Expand All @@ -102,14 +167,6 @@ async def async_step_title(self, user_input=None):
if not user_input:
return self.async_show_form(step_id="title", data_schema=data_schema)

data = {**self._data, **user_input}

return self.async_create_entry(title=data[CONF_TITLE], data=data)


class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""

self._data.update(user_input)

class InvalidAuth(exceptions.HomeAssistantError):
"""Error to indicate there is invalid auth."""
return self.async_create_entry(title=self._data[CONF_TITLE], data=self._data)
10 changes: 6 additions & 4 deletions custom_components/miner/const.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""Constants for the Miner integration."""

DOMAIN = "miner"

CONF_IP = "ip"
CONF_TITLE = "title"
CONF_PASSWORD = "password"
CONF_USERNAME = "username"
CONF_SSH_PASSWORD = "ssh_password"
CONF_SSH_USERNAME = "ssh_username"
CONF_RPC_PASSWORD = "rpc_password"
CONF_WEB_PASSWORD = "web_password"
CONF_WEB_USERNAME = "web_username"

DEVICE_CLASS_HASHRATE = "hashrate"
DEVICE_CLASS_EFFICIENCY = "efficiency"
TERA_HASH_PER_SECOND = "TH/s"
JOULES_PER_TERA_HASH = "J/TH"
42 changes: 33 additions & 9 deletions custom_components/miner/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

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

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -37,18 +44,35 @@ def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
),
)

@property
def available(self):
"""Return if device is available or not."""
return self.miner is not None

async def _async_update_data(self):
"""Fetch sensors from miners."""

miner_ip = self.entry.data[CONF_IP]
miner_username = self.entry.data[CONF_USERNAME]
miner_password = self.entry.data[CONF_PASSWORD]
if self.miner is None:
self.miner = await pyasic.get_miner(miner_ip)

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

if self.miner is None:
raise UpdateFailed("Miner Offline")

try:
if self.miner is None:
self.miner = await pyasic.get_miner(miner_ip)
self.miner.username = miner_username
self.miner.pwd = miner_password
if self.miner.api is not None:
if self.miner.api.pwd is not None:
self.miner.api.pwd = self.entry.data.get(CONF_RPC_PASSWORD, "")

if self.miner.web is not None:
self.miner.web.username = self.entry.data.get(CONF_WEB_USERNAME, "")
self.miner.web.pwd = self.entry.data.get(CONF_WEB_PASSWORD, "")

if self.miner.ssh is not None:
self.miner.ssh.username = self.entry.data.get(CONF_SSH_USERNAME, "")
self.miner.ssh.pwd = self.entry.data.get(CONF_SSH_PASSWORD, "")

miner_data = await self.miner.get_data(
include=[
Expand All @@ -69,9 +93,9 @@ async def _async_update_data(self):
raise UpdateFailed("API Error") from err

except Exception as err:
raise UpdateFailed("API Error") from err
raise UpdateFailed("Unknown Error") from err

_LOGGER.debug(miner_data)
_LOGGER.debug(f"Got data: {miner_data}")

data = {
"hostname": miner_data.hostname,
Expand Down
4 changes: 2 additions & 2 deletions custom_components/miner/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"homekit": {},
"iot_class": "local_polling",
"issue_tracker": "https://github.com/Schnitzel/hass-miner/issues",
"requirements": ["pyasic==0.46.0"],
"requirements": ["pyasic==0.48.5"],
"ssdp": [],
"version": "1.1.0",
"version": "1.1.1b4",
"zeroconf": []
}
Loading

0 comments on commit 3fe0438

Please sign in to comment.