Skip to content

Fix DG-Home1 Elphapex detection and harden hashboard parsing#429

Open
purcell-lab wants to merge 4 commits into
UpstreamData:masterfrom
purcell-lab:fix/dg-home1-support
Open

Fix DG-Home1 Elphapex detection and harden hashboard parsing#429
purcell-lab wants to merge 4 commits into
UpstreamData:masterfrom
purcell-lab:fix/dg-home1-support

Conversation

@purcell-lab
Copy link
Copy Markdown

Fixes the "partially supported" warning and TypeError on Elphapex DG-Home1 miners (firmware DG-Home1_V1.0.5).

Closes #428. Refs #311.

Problem

On pyasic 0.79.0, scanning a DG-Home1 emits:

UserWarning: Partially supported miner found: DG-Home1, type: MinerTypes.ELPHAPEX, ...
[Elphapex (Stock): 192.168.x.x]

…and any code path that touches hashboards raises APIError wrapping a TypeError: 'NoneType' object cannot be interpreted as an integer from range(self.expected_hashboards) in ElphapexMiner._get_hashboards.

Two underlying issues:

  1. Model detection. get_miner_model_elphapex only reads web_json_data["minertype"] from /cgi-bin/get_system_info.cgi. DG-Home1 firmware V1.0.5 doesn't expose that key. The device's INFO.type from /cgi-bin/stats.cgi is "DG-Home1", but the lookup table key is "DG1-Home" — and _select_miner_from_classes uppercases the model before lookup, so even the existing "DG1-Home" key would never match ("DG1-Home".upper() == "DG1-HOME").
  2. Hashboard parsing. When the model falls through to the partial-support fallback class, expected_hashboards is None, and range(None) raises TypeError. Even with the correct model resolved, DG-Home1 returns 3 empty chains and 1 populated one; the existing parser also divides by len(chip_temp_data) without guarding the empty case.

Fix

pyasic/miners/factory.py

  • Lookup-table keys for MinerTypes.ELPHAPEX are now uppercase ("DG1-HOME") to match the case-insensitive lookup in _select_miner_from_classes. "DG-HOME1" is added as an alias so the raw firmware string also resolves directly.
  • get_miner_model_elphapex now:
    • Tries minertype, then falls back to type, model, hostname from /cgi-bin/get_system_info.cgi.
    • If still unresolved, queries /cgi-bin/stats.cgi and reads INFO.type / INFO.model / INFO.miner_version.
    • Normalises the result via _normalize_elphapex_model (strips firmware-version suffix like _V1.0.5, remaps "DG-Home1""DG1-Home").

pyasic/miners/backends/elphapex.py

_get_hashboards is hardened:

  • Fetches web_stats first, then derives expected_hashboards from STATS[0].chain_num (or len(chain)) when the device is partially supported. Prevents TypeError from range(None).
  • Skips out-of-range / non-int board indices instead of raising.
  • Guards the chip_temp average against empty temp_chip lists (the original divide-by-zero) and against non-numeric entries.
  • Uses .get() for sn, asic_num, temp_pcb, temp_chip so a missing key on partial firmware no longer aborts the whole loop.
  • Sets hb.missing = True when asic_num == 0, so the 3 unpopulated chains on DG-Home1 are reported as missing rather than as healthy zero-chip boards.

Verification

Inline checks against the captured DG-Home1 stats.cgi payload (from #311) — 1 populated chain at index 3, 3 empty chains:

slot 0 chips 0   missing True   temp 56.5 chip_temp None  sn None                       hashrate 0.0 GH/s
slot 1 chips 0   missing True   temp 56.5 chip_temp None  sn None                       hashrate 0.0 GH/s
slot 2 chips 0   missing True   temp 56.5 chip_temp None  sn None                       hashrate 0.0 GH/s
slot 3 chips 120 missing False  temp 56.5 chip_temp 49.37 sn 11HY251116N300885H11LD22   hashrate 2.15 GH/s

Normaliser table:

Input Output
DG-Home1 DG1-Home
DG-HOME1 DG1-Home
DG-Home1_V1.0.5 DG1-Home
DG1-Home DG1-Home
DG1+ DG1+
DG1 DG1

All map to ElphapexDG1Home via the lookup table after upper-casing.

Existing test suite passes (25 passed, network/local/rpc test groups skipped per their usual harness).

ruff check and ruff format --check both clean on the touched files.

Notes / out of scope

  • DG-Home1's miner.rpc is None, so RPC-based methods (devdetails, version) remain unavailable. Documented in the API reference comment on Update request: DG-Home1 still partially supported in pyasic 0.79.0 #428.
  • expected_chips=120 already matches the live device (asic_num=120 on chain 3), so no model-class changes were needed once detection works.
  • setworkmode.cgi / getworkmode.cgi integration is intentionally not included here — happy to follow up in a separate PR if desired.

- factory.py: uppercase ELPHAPEX lookup keys (case-insensitive lookup);
  add DG-HOME1 alias; get_miner_model_elphapex falls back to
  type/model/hostname from get_system_info.cgi and to
  INFO.type/INFO.model/INFO.miner_version from stats.cgi; new
  _normalize_elphapex_model helper maps 'DG-Home1' / 'DG-Home1_V1.0.5'
  to 'DG1-Home'.
- backends/elphapex.py _get_hashboards: derive expected_hashboards from
  STATS[0].chain_num when None (prevents range(None) TypeError);
  skip out-of-range board indices; guard chip_temp averaging against
  empty/non-numeric temp_chip entries; use .get() throughout; set
  hb.missing=True when asic_num == 0.

Closes UpstreamData#428. Refs UpstreamData#311.
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 10, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 37 complexity

Metric Results
Complexity 37

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

CodeFactor flagged the previous implementation as a Complex Method
(cyclomatic complexity 23). Split into small focused helpers:

- _resolve_expected_hashboards: derive slot count from stats.cgi when
  expected_hashboards is None on partially-supported devices.
- _iter_stats_chains: safe access to STATS[0].chain.
- _apply_chain_to_board: populate one HashBoard from a chain dict.
- _set_board_hashrate / _average_pcb_temp / _average_chip_temp:
  isolated, individually testable building blocks.

_get_hashboards itself drops from cyclomatic complexity 23 (radon: D 30)
to 6 (radon: B 6); no method in the file exceeds 9. Behaviour is
preserved: same outputs on the DG-Home1 stats payload, same fallback to
chain_num for partial-support, same APIError-safe path when stats are
unavailable. Added defensive isinstance/type guards so malformed
payloads (missing STATS, non-list 'chain', non-int 'index', non-list
temp arrays) no longer raise.
Codacy flagged 3 multi-line docstrings (D213: 'Multi-line docstring
summary should start at the second line'). Move the summary line below
the opening triple-quote on:

- ElphapexMiner._get_hashboards
- ElphapexMiner._resolve_expected_hashboards
- ElphapexMiner._average_chip_temp
- MinerFactory._normalize_elphapex_model

Also adds short single-line docstrings to _set_board_hashrate and
_average_pcb_temp for consistency with the surrounding helpers.
Codacy enforces both pydocstyle D212 ('summary on first line') and D213
('summary on second line'), which are mutually exclusive for any
multi-line docstring. Collapse the four affected docstrings to single
lines and move the elaboration into adjacent comments. No behaviour
change; ruff format and ruff check both clean; existing tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update request: DG-Home1 still partially supported in pyasic 0.79.0

1 participant