A proper Home Assistant custom integration for monitoring ESP32-based Bitcoin miners (NMMiner firmware).
- ποΈ UI Configuration - Set up via Home Assistant UI (no YAML needed!)
- π Real-time Updates - UDP push notifications every 5 seconds
- π Auto-discovery - Miners automatically appear as devices
- π Block Hit Events - Get notified when any miner finds a valid block
- π Comprehensive Sensors - Hashrate, shares, temperature, WiFi signal, and more
- ποΈ Proper Architecture - Uses DataUpdateCoordinator, config entries, and modern HA patterns
- Open HACS
- Go to Integrations
- Click the three dots in the top right
- Select "Custom repositories"
- Add this repository URL
- Install "NMMiner"
- Restart Home Assistant
- Copy the
custom_components/nmminerfolder to your Home Assistant'sconfig/custom_components/directory - Restart Home Assistant
- Go to Configuration β Integrations
- Click "+ Add Integration"
- Search for "NMMiner"
-
After adding the integration, you'll be prompted for:
- UDP Port: The port your miners broadcast on (default: 12345)
-
Click Submit
-
Your miners will auto-discover within seconds!
For each discovered miner (e.g., 192.168.1.101), you get:
sensor.nmminer_192_168_1_101_hashrate- Current hashrate (H/s)sensor.nmminer_192_168_1_101_shares- Accepted/Total sharessensor.nmminer_192_168_1_101_valid_blocks- Blocks found βsensor.nmminer_192_168_1_101_best_difficulty- Best share difficultysensor.nmminer_192_168_1_101_temperature- Device temperature (Β°C)sensor.nmminer_192_168_1_101_wifi_signal- RSSI (dBm)sensor.nmminer_192_168_1_101_uptime- Miner uptimesensor.nmminer_192_168_1_101_progress- Current work progress (%)sensor.nmminer_192_168_1_101_firmware_version- Firmware version
Some sensors include additional data in their attributes:
Shares sensor:
accepted- Number of accepted sharestotal- Total shares submittedrejection_rate- Percentage of rejected shares
Best Difficulty sensor:
pool_diff- Current pool difficultylast_diff- Last share difficultynet_diff- Network difficulty
Firmware Version sensor:
board_type- Type of ESP32 boardfree_heap- Available memory
Fired whenever any miner's Valid block counter increases.
Event Data:
{
"miner_ip": "192.168.1.101",
"valid_blocks": 1,
"best_diff": "4.021M",
"hashrate": "113.13K"
}automation:
- alias: "π NMMiner Block Found"
trigger:
- platform: event
event_type: nmminer_block_found
action:
- service: notify.mobile_app_your_phone
data:
title: "π Bitcoin Block Found!"
message: >
Miner {{ trigger.event.data.miner_ip }} found a valid block!
Valid Blocks: {{ trigger.event.data.valid_blocks }}
Best Diff: {{ trigger.event.data.best_diff }}
Hashrate: {{ trigger.event.data.hashrate }}
data:
priority: highautomation:
- alias: "Miner Block Counter"
trigger:
- platform: state
entity_id: sensor.nmminer_192_168_1_101_valid_blocks
condition:
- condition: template
value_template: "{{ trigger.to_state.state | int > trigger.from_state.state | int }}"
action:
- service: notify.mobile_app_your_phone
data:
message: "Miner 1 found a block! Total: {{ trigger.to_state.state }}"automation:
- alias: "NMMiner Overheating"
trigger:
- platform: numeric_state
entity_id: sensor.nmminer_192_168_1_101_temperature
above: 65
action:
- service: notify.mobile_app_your_phone
data:
title: "β οΈ Miner Overheating"
message: "Miner is at {{ trigger.to_state.state }}Β°C!"automation:
- alias: "NMMiner Offline"
trigger:
- platform: state
entity_id:
- sensor.nmminer_192_168_1_101_hashrate
- sensor.nmminer_192_168_1_102_hashrate
to: 'unavailable'
for:
minutes: 2
action:
- service: notify.mobile_app_your_phone
data:
title: "β οΈ Miner Offline"
message: "{{ trigger.to_state.name }} stopped broadcasting"template:
- sensor:
- name: "Total Blocks Found"
unique_id: nmminer_total_blocks
state: >
{% set ns = namespace(total=0) %}
{% for state in states.sensor
if 'nmminer' in state.entity_id
and 'valid_blocks' in state.entity_id
and state.state not in ['unknown', 'unavailable'] %}
{% set ns.total = ns.total + (state.state | int(0)) %}
{% endfor %}
{{ ns.total }}
icon: mdi:bitcointemplate:
- sensor:
- name: "Total Mining Hashrate"
unique_id: nmminer_total_hashrate
unit_of_measurement: "KH/s"
state_class: measurement
state: >
{% set ns = namespace(total=0.0) %}
{% for state in states.sensor
if 'nmminer' in state.entity_id
and 'hashrate' in state.entity_id
and state.state not in ['unknown', 'unavailable'] %}
{% set ns.total = ns.total + (state.state | float(0)) %}
{% endfor %}
{{ (ns.total / 1000) | round(2) }}
icon: mdi:chiptype: vertical-stack
cards:
- type: markdown
content: |
# π Bitcoin Mining Farm
**Total Blocks:** {{ states('sensor.total_blocks_found') | int(0) }}
**Total Hashrate:** {{ states('sensor.total_mining_hashrate') }} KH/s
- type: entities
title: "Miner Status"
entities:
- entity: sensor.nmminer_192_168_1_101_hashrate
name: "Miner 1 Hashrate"
- entity: sensor.nmminer_192_168_1_101_valid_blocks
name: "Miner 1 Blocks"
- entity: sensor.nmminer_192_168_1_101_temperature
name: "Miner 1 Temperature"
- entity: sensor.nmminer_192_168_1_102_hashrate
name: "Miner 2 Hashrate"
- entity: sensor.nmminer_192_168_1_102_valid_blocks
name: "Miner 2 Blocks"
- entity: sensor.nmminer_192_168_1_102_temperature
name: "Miner 2 Temperature"
- type: history-graph
title: "Hashrate History"
hours_to_show: 24
entities:
- sensor.nmminer_192_168_1_101_hashrate
- sensor.nmminer_192_168_1_102_hashrate- Make sure you've restarted Home Assistant after copying files
- Check the logs for errors: Settings β System β Logs
-
Verify UDP broadcasts are reaching HA:
sudo tcpdump -i any -n udp port 12345 -A
-
Check firewall:
sudo ufw allow 12345/udp
-
Verify miners are broadcasting:
- Access miner's web interface
- Confirm firmware version supports broadcasts (v0.3.01+)
- Wait 30 seconds for first broadcast
- Check Home Assistant logs for errors
- Ensure miners and HA are on same network/VLAN
- Settings β Integrations β NMMiner β Delete
- Restart Home Assistant
- Re-add the integration
- Architecture: Modern HA integration using DataUpdateCoordinator
- Protocol: UDP broadcasts on configurable port (default 12345)
- Update Method: Push-based (miners broadcast every 5 seconds)
- Data Format: JSON payload
- Device Discovery: Automatic based on IP in broadcast
This integration follows Home Assistant's integration quality scale and best practices:
- β Config flow for UI-based setup
- β DataUpdateCoordinator for data management
- β CoordinatorEntity for sensors
- β Proper device registry entries
- β Unique IDs for all entities
- β Event-driven architecture
- β Async/await throughout
- NMMiner Firmware: https://github.com/NMminer1024/NMMiner
- Telegram: https://t.me/NMMiner
- Home Assistant Community: https://community.home-assistant.io/
MIT License