Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update pyasic version, and attempt to fix duplicate entities #292

Merged
merged 21 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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