UpstreamData / pyasic

A simplified and standardized interface for Bitcoin ASICs.
https://docs.pyasic.org
Apache License 2.0
100 stars 55 forks source link

Switching on flashing lights and setting low power mode feature requests #49

Closed gav1111 closed 1 year ago

gav1111 commented 1 year ago

Is there a global command to 1) switch on and off flashing lights to identify miner 2) set the miner into low power mode (for new antminer firmwares for example) 3) take the miner out of low power mode back into normal power mode

on each miner for different miner apis?

UpstreamData commented 1 year ago

Global command for lights is faultlight{on/off}, see the docs here.

Handling LPM is a bit tougher. There are machines that use LPM, some machines that have Autotuning and fine grained control, and some other machines that allow frequency and voltage control. You best bet is to call miner.get_config, and then set config.autotuning_wattage and config.miner_mode. The docs don't show miner_mode, I will have to update that, but if you click the view source code here you must set it to a variant of the enum X19PowerMode. See docs here..

The opposite of the above goes for resetting it.

UpstreamData commented 1 year ago

I think I'm going to mark this as completed, but for reference -

Fault light commands exist as fault_light_on and fault_light_off

Setting LPM/NPM with a method is not planned, because I feel that should be delegated to the user where it is much less opaque what the function is actually doing. I feel it is more counterintuitive to have a set_LPM command on miners which support autotuning, since it would cause a side effect (downclocking) that isn't expected when a miner doesn't have LPM.

LPM/NPM can also easily be accomplished with MinerConfig and the get_config method, then setting autotuning_wattage and miner_mode, which makes it very clear to the user what they are doing and allows proper customization.

gav1111 commented 1 year ago

flashing works great thanks, setting low power still figuring that out

gav1111 commented 1 year ago

Can I have a little assistance in helping to script how to set low power mode on S19 antminers with new firmware which has low power mode. I am trying the following to get the get_config per what you say above, but getting get_config isnt defined:

import asyncio

from pyasic import get_miner
from pyasic import MinerConfig

async def get_api_commands(miner_ip: str):
    # Get the miner
    miner = await get_miner(miner_ip)

    # List all available commands
    # Can also be called explicitly with the function miner.api.get_commands()
    print(miner.api.commands)

if __name__ == "__main__":
    asyncio.run(get_config("172.16.1.86"))
UpstreamData commented 1 year ago

The get_config() method is called on a miner object. Call miner = await get_miner(IP) first, then call cfg = await miner.get_config()

gav1111 commented 1 year ago

Yep thank you, I figured that one out now and I see that it can be either 'miner_mode=<X19PowerMode.Normal: 0>' or 'miner_mode=<X19PowerMode.LPM: 3>' Then how do I issue it send_config to switch from normal to lpm?

UpstreamData commented 1 year ago
from pyasic.config import X19PowerMode
import pyasic

async def set_lpm(ip: str):
    miner = await pyasic.get_miner(ip)
    cfg = await miner.get_config()
    cfg.miner_mode = X19PowerMode.LPM
    await miner.send_config(cfg)

async def set_normal(ip: str):
    miner = await pyasic.get_miner(ip)
    cfg = await miner.get_config()
    cfg.miner_mode = X19PowerMode.Normal
    await miner.send_config(cfg)
gav1111 commented 1 year ago

I think its working, I had to add await miner.reboot() after the send_config otherwise it was just hanging, and then it goes through the following which I dont think I ve seen before, and after a few minutes back up and running

2023-06-29 22:49:19 enable_power_calibration,calibration date:211208.
2023-06-29 22:49:20 power type version: 0x0075
2023-06-29 22:49:21 disable power watchdog: 0x0000
2023-06-29 22:49:22 Initializing the power, please wait, this may take up to 2 minutes...
2023-06-29 22:52:23 Slept 180 seconds, diff = 4.
2023-06-29 22:52:27 set_voltage_by_steps to 1500.
2023-06-29 22:52:37 start up min temp by 75a = 38
gav1111 commented 1 year ago

I am using the following python to set a compatible miner into low power mode

import sys
import asyncio
from pyasic.config import X19PowerMode
import pyasic

IP = sys.argv[1]

async def set_lpm(ip: str):
    miner = await pyasic.get_miner(ip)
    cfg = await miner.get_config()
    cfg.miner_mode = X19PowerMode.LPM
    await miner.send_config(cfg)
    await miner.reboot()

if __name__ == "__main__":
    asyncio.run(set_lpm(IP))

yesterday it was working for S19 Pro, but I am not sure for S19j Pro, today on 3 occasions it didnt set 3 S19j Pro into low power mode, and I had to do it manually. How should I try to debug it?

gav1111 commented 1 year ago

S19j Pro reports the setting in the same way, just refuses to set it miner_mode=<X19PowerMode.LPM: 3>

UpstreamData commented 1 year ago

Does it let you set it via the web interface? Some miners don't support sleep mode for whatever reason.

gav1111 commented 1 year ago

Yes I had to do it via gui. It went into low power mode by choosing it via gui. So it’s not sleep mode I’m working on right now but the new low power that ant miner introduced dec 2022. Should I try setting it into sleep mode via script to see if it will react to that?

On Sat, Jul 1, 2023 at 01:22, UpstreamData @.***> wrote:

Does it let you set it via the web interface? Some miners don't support sleep mode for whatever reason.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you modified the open/close state.Message ID: @.***>

gav1111 commented 1 year ago

I am setting power back to normal now via script. S19 pro set via script, s19j pro didn’t and I had to go into gui to do it manually

On Sat, Jul 1, 2023 at 01:22, UpstreamData @.***> wrote:

Does it let you set it via the web interface? Some miners don't support sleep mode for whatever reason.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you modified the open/close state.Message ID: @.***>

gav1111 commented 1 year ago

I managed to get the following error, maybe will be of help to understand:

Command failed: python3 /home/nodered/py/lpm.py 192.168.0.7
Traceback (most recent call last):
  File "/home/nodered/py/lpm.py", line 16, in <module>
    asyncio.run(set_lpm(IP))
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/nodered/py/lpm.py", line 10, in set_lpm
    cfg = await miner.get_config()
AttributeError: 'NoneType' object has no attribute 'get_config'
gav1111 commented 1 year ago

and here is the code

~/pyasic$ cat lpm.py
import sys
import asyncio
from pyasic.config import X19PowerMode
import pyasic

IP = sys.argv[1]

async def set_lpm(ip: str):
    miner = await pyasic.get_miner(ip)
    cfg = await miner.get_config()
    cfg.miner_mode = X19PowerMode.LPM
    await miner.send_config(cfg)
    await miner.reboot()

if __name__ == "__main__":
    asyncio.run(set_lpm(IP))

so is it not responding to pyasic.get_miner()?

gav1111 commented 1 year ago

Maybe in that case it didnt, but it prints cfg without any issues, config just doesnt "stick":

:~/temp/pyasic$ python3 find_powermode.py 192.168.0.7
MinerConfig(pool_groups=[_PoolGroup(quota=1, group_name='QYR225', pools=[_Pool(url='.com:3333', username='sa.1', password=''), _Pool(url='', username='', password=''), _Pool(url='', username='', password='')])], temp_mode='auto', temp_target=70.0, temp_hot=80.0, temp_dangerous=100.0, minimum_fans=None, fan_speed=None, asicboost=None, miner_mode=<X19PowerMode.Normal: 0>, autotuning_enabled=True, autotuning_mode=None, autotuning_wattage=None, autotuning_hashrate=None, dps_enabled=None, dps_power_step=None, dps_min_power=None, dps_shutdown_enabled=None, dps_shutdown_duration=None)
UpstreamData commented 1 year ago

so is it not responding to pyasic.get_miner()?

It appears it's not responding, yeah. Your first script isn't getting the config at all (you can show this by printing the config right after you try to get it, it won't even make it there), but your second script is working fine.

I wonder if it could somehow be related to environments or something, if one of the scripts is running as root and the other as your user and they are on different versions of pyasic? Try sudo pip install -U pyasic and pip install -U pyasic and see if it changes anything?

gav1111 commented 1 year ago

I think the one I got an error for was a one off. I run the python script from node-red. If I run the script for an S19 Pro, it works, if I run it for S19j Pro it doesnt work (no error, just executes with code=0, but no effect on the miner)

UpstreamData commented 1 year ago

It's possible they have different values for LPM across miners? Can you send the result of await miner.web.get_miner_conf() before and after manually setting LPM via the web interface on the miner that isn't working?

gav1111 commented 1 year ago

here is low power mode on S19j Pro:

{'pools': [{'url': ':3333', 'user': 'sa', 'pass': ''}, {'url': '', 'user': '', 'pass': ''}, {'url': '', 'user': '', 'pass': ''}], 'api-listen': True, 'api-network': True, 'api-groups': 'A:stats:pools:devs:summary:version', 'api-allow': 'A:0/0,W:*', 'bitmain-fan-ctrl': False, 'bitmain-fan-pwm': '100', 'bitmain-use-vil': True, 'bitmain-freq': '400', 'bitmain-voltage': '1360', 'bitmain-ccdelay': '0', 'bitmain-pwth': '1', 'bitmain-work-mode': '3', 'bitmain-freq-level': '100'}
gav1111 commented 1 year ago

and normal power mode on S19j Pro is:

{'pools': [{'url': '333', 'user': 'sa', 'pass': ''}, {'url': '', 'user': '', 'pass': ''}, {'url': '', 'user': '', 'pass': ''}], 'api-listen': True, 'api-network': True, 'api-groups': 'A:stats:pools:devs:summary:version', 'api-allow': 'A:0/0,W:*', 'bitmain-fan-ctrl': False, 'bitmain-fan-pwm': '100', 'bitmain-use-vil': True, 'bitmain-freq': '400', 'bitmain-voltage': '1360', 'bitmain-ccdelay': '0', 'bitmain-pwth': '1', 'bitmain-work-mode': '0', 'bitmain-freq-level': '100'}
UpstreamData commented 1 year ago

ccdelay and pwth are new to me, maybe I have to add them when submitting... I'll see what I can figure out, but it's seeming like they might have changed something.

gav1111 commented 1 year ago

S19 Pro also has those, but the script works on them

{'pools': [{'url': '..:', 'user': '.', 'pass': '123'}, {'url': '..:', 'user': '', 'pass': '123'}, {'url': '..:', 'user': '.', 'pass': '123'}], 'api-listen': True, 'api-network': True, 'api-groups': 'A:stats:pools:devs:summary:version', 'api-allow': 'A:0/0,W:*', 'bitmain-fan-ctrl': False, 'bitmain-fan-pwm': '100', 'bitmain-use-vil': True, 'bitmain-freq': '400', 'bitmain-voltage': '1420', 'bitmain-ccdelay': '0', 'bitmain-pwth': '1', 'bitmain-work-mode': '0', 'bitmain-freq-level': '100'}
UpstreamData commented 1 year ago

Can you try updating the config and tracking the network request it sends to set_miner_conf.cgi via the web interface? Settings page, CTRL+SHIFT+I, Dev tools "Network" tab, should be recording, if not hit record top left, then hit the submit button. It should output the request it sends to that page, I'm looking for the payload data. Trying to figure out if something changed in some new version of firmware or something.

gav1111 commented 1 year ago

{ "bitmain-fan-ctrl": false, "bitmain-fan-pwm": "100", "miner-mode": 3, "freq-level": "100", "pools": [ { "url": "xxx", "user": "xx.xxx", "pass": "" }, { "url": "", "user": "", "pass": "" }, { "url": "", "user": "", "pass": "" } ] }

gav1111 commented 1 year ago

is this what you wanted to see, or do I need to do something else?

UpstreamData commented 1 year ago

Yep, that's perfect. Sorry, been away for the past few days so responding when I can. I have to do some testing now, it looks the same but it might be an issue with setting bitmain-freq. We shall see.

gav1111 commented 1 year ago

and this is the payload for setting low power mode for S19 Pro

{
    "bitmain-fan-ctrl": false,
    "bitmain-fan-pwm": "100",
    "miner-mode": 3,
    "freq-level": "100",
    "pools": [
        {
            "url": "",
            "user": "",
            "pass": ""
        },
        {
            "url": "",
            "user": "",
            "pass": ""
        },
        {
            "url": "",
            "user": "",
            "pass": "123"
        }
    ]
}

arent they the same? So we havent figured it?

UpstreamData commented 1 year ago

Yeah same thing. My guess is that some miners respond differently to setting frequency mode. I'll test later.

UpstreamData commented 1 year ago

Can you test with v0.36.12? Seems like it may have been an issue with some versions not liking a string formatted miner mode value.

gav1111 commented 1 year ago

I upgraded to 0.37 and I am getting some general errors, what I can see right away is that hashrate isnt reported correctly

make="AntMiner",hashrate=30.24

should be 100

gav1111 commented 1 year ago

however, with 0.37 it appears that I was able to set low power mode for an s19j pro, will report if it still works on others

UpstreamData commented 1 year ago

Good catch on the hashrate, I'll get that fixed here shortly.

UpstreamData commented 1 year ago

Fixed the addition issue in 0.37.1

gav1111 commented 1 year ago

upgraded to 0.37.1, hashrate works. Setting S19j Pro into low power doesnt work. Setting S19 Pro into low power still works. I dont know why script worked on 0.37 for one particular S19j Pro, I tried 0.37 on another S19j Pro and it didnt work

UpstreamData commented 1 year ago

What firmware version are they on? Can you get them all on the same firmware version (preferably the latest version) and test? I think what happened is they changed the value sent to power mode from a string to an integer, or vice versa, and so it has to either be quoted or unquoted depending on the version of the firmware.

gav1111 commented 1 year ago

It doesn’t set it to low power but it does something because it goes through power recalibration

On Fri, Jul 7, 2023 at 21:10, UpstreamData @.***> wrote:

Fixed the addition issue in 0.37.1

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you modified the open/close state.Message ID: @.***>

gav1111 commented 1 year ago

S19 Pro: Firmware Version Mon Dec 26 17:10:01 CST 2022 S19j Pro that worked with 0.37: Firmware Version Mon Dec 26 19:16:16 CST 2022 S19j Pro that didnt work with 0.37.1 or 0.37: Firmware Version Mon Dec 26 17:19:30 CST 2022 S19j Pro that didnt work with 0.37.1: Firmware Version Mon Dec 26 19:24:49 CST 2022

I didnt think they were on different firmwares, I only have one file for all of them: Antminer-S19j-Pro-merge-release-20221226125147.bmu I ll try to run this file on them again but not sure if it ll change anything

gav1111 commented 1 year ago

I did firmware upgrade on this one S19j Pro that didnt work with 0.37.1 or 0.37: Firmware Version Mon Dec 26 17:19:30 CST 2022 with the file name above, it didnt change anything. I dont know why they report different time if they are upgraded to the same firmware file.

UpstreamData commented 1 year ago

This is what I like to call a Bitmain moment. Seems like they have a merge release which downloads from a server and gets slightly different builds based on some arbitrary information on the miner. This means it will likely be nearly impossible to rectify this, as it's not really possible to tell what is supposed to be sent for any given system. Your best bet might be to set up a dict mapping to whether it needs to be a string or an int, then manually calling miner.web.set_miner_conf on the miners with the result of config.as_x19 and setting miner_mode to either a str or int. I'll spend some time here writing something up.

gav1111 commented 1 year ago

Do you think running python3 lpm.py 192.168.0.21 s19j pro can be sufficient to get it to work? And then in lpm.py for s19j pro an integer is set and if s19 pro a string? Or can script choose because ip is passed so it can just query without model argument?

On Fri, Jul 7, 2023 at 23:00, UpstreamData @.***> wrote:

This is what I like to call a Bitmain moment. Seems like they have a merge release which downloads from a server and gets slightly different builds based on some arbitrary information on the miner. This means it will likely be nearly impossible to rectify this, as it's not really possible to tell what is supposed to be sent for any given system. Your best bet might be to set up a dict mapping to whether it needs to be a string or an int, then manually calling miner.web.set_miner_conf on the miners with the result of config.as_x19 and setting miner_mode to either a str or int. I'll spend some time here writing something up.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you modified the open/close state.Message ID: @.***>

UpstreamData commented 1 year ago

I wouldn't, I would pass either str or int as a value to the script, so ./LPM.py 192.168.1.10 int, but it makes more sense IMO to map it out.

gav1111 commented 1 year ago

and because I am not that proficient to program it properly, I could just run the script twice, once with int and once with str, and then reboot and it will give the necessary result.

UpstreamData commented 1 year ago

Here ya go. You can use this using either a map like the top, or it can be slightly modified to take "int" or "str" as an argument to the file and it will still work, you just have to pass it into mode_type instead of the value in the dict.

import pyasic
import asyncio
from pyasic.config import X19PowerMode

MINER_MODE_TYPES = {
    "10.0.1.6": int,  # uses int type
    "192.168.1.11": str,  # uses str type
    "172.16.1.16": "str"  # uses str type from string
}

async def send_config(miner: pyasic.AnyMiner, cfg: pyasic.MinerConfig, mode_type):
    new_cfg = cfg.as_x19()

    if isinstance(mode_type, type):
        new_cfg["miner-mode"] = mode_type(cfg.miner_mode.value)
    elif isinstance(mode_type, str):
        if mode_type == "str":
            new_cfg["miner-mode"] = str(cfg.miner_mode.value)
        elif mode_type == "int":
            new_cfg["miner-mode"] = int(cfg.miner_mode.value)

    data = await miner.web.set_miner_conf(cfg)

    if data:
        if data.get("code") == "M000":
            return

    for i in range(7):
        data = await miner.get_config()
        if data == cfg:
            break
        await asyncio.sleep(1)

async def set_lpm(ip: str):
    miner = await pyasic.get_miner(ip)
    config = await miner.get_config()
    config.miner_mode = X19PowerMode.LPM
    await send_config(miner, config, MINER_MODE_TYPES.get(ip, str))

async def set_normal(ip: str):
    miner = await pyasic.get_miner(ip)
    config = await miner.get_config()
    config.miner_mode = X19PowerMode.Normal
    await send_config(miner, config, MINER_MODE_TYPES.get(ip, str))

async def main():
    await set_lpm("10.0.1.6")
    await set_normal("192.168.1.11")

asyncio.run(main())
gav1111 commented 1 year ago

Thank you. Using your script I am trying to do some more testing, I am running the following script

import sys
import asyncio
from pyasic.config import X19PowerMode
import pyasic

IP = sys.argv[1]

async def set_lpm(ip: str):
    miner = await pyasic.get_miner(ip)
    cfg = await miner.get_config()
    cfg.miner_mode = X19PowerMode.LPM
    new_cfg = cfg.as_x19()
    new_cfg["miner-mode"] = str(cfg.miner_mode.value)
    print(new_cfg)
    data = await miner.web.set_miner_conf(new_cfg)
    print(data)

if __name__ == "__main__":
    asyncio.run(set_lpm(IP))

it set low power for s19 pro and they returned data as {'stats': 'success', 'code': 'M000', 'msg': 'OK!'} for s19j pro it hasnt failed to set low power, but sometimes I need to call this script twice, maybe 30seconds apart, they always return None but seems to set low power. Does it give you any clues?

UpstreamData commented 1 year ago

That's what this part is designed to do, it should ensure theyre set into LPM.

    if data:
        if data.get("code") == "M000":
            return

    for i in range(7):
        data = await miner.get_config()
        if data == cfg:
            break
        await asyncio.sleep(1)
gav1111 commented 1 year ago

I ll ask about the loop tomorrow, but what do you think about them all setting to low power mode when passing miner_mode value as string?

UpstreamData commented 1 year ago

I ll ask about the loop tomorrow, but what do you think about them all setting to low power mode when passing miner_mode value as string?

Not sure what you mean by this?

gav1111 commented 1 year ago

What I wanted to ask about the loop, is how does it ensure the setting sticks? Does it do set_miner_conf again if data != cfg?

UpstreamData commented 1 year ago

Just replace the break and return in that code with return True, then you can check if the result is True and retry if needed.