Skip to content

Conversation

b-rowan
Copy link

@b-rowan b-rowan commented Aug 29, 2025

Super basic starter implementation of a miner adapter using pyasic. Good chance it is completely broken since I am using asyncio.run to run all tasks, and I also likely missed some stuff, but a good start for feedback.

@b-rowan
Copy link
Author

b-rowan commented Aug 29, 2025

CC: #3

@markoceri
Copy link
Collaborator

Thanks @b-rowan for your contribution

@markoceri
Copy link
Collaborator

We have a conflict with dependencies:

The conflict is caused by:
    The user requested pydantic==2.8.2
    pydantic-settings 2.8.1 depends on pydantic>=2.7.0
    fastapi 0.115.12 depends on pydantic!=1.8, !=1.8.1, !=2.0.0, !=2.0.1, !=2.1.0, <3.0.0 and >=1.7.4
    homeassistant-api 5.0.0 depends on pydantic<2.9 and >=2.0
    pyasic 0.76.5 depends on pydantic>=2.11.0

the issue is with homeassistant-api and pyasic, they require an incompatible version of pydantic

@markoceri markoceri self-assigned this Sep 3, 2025
@markoceri markoceri linked an issue Sep 3, 2025 that may be closed by this pull request
@markoceri
Copy link
Collaborator

Digging deeper I saw that It is possible to downgrade homeassistant-api version and upgrade pydantic version.

Modified versions:

  • homeassistant-api: 4.2.2.post1
  • pydantic: 2.11.0

Use this branch to test: https://github.com/edge-mining/core/tree/feature-add-pyasic-miner-adapter

@b-rowan
Copy link
Author

b-rowan commented Sep 3, 2025

If it's a huge deal I could in theory downgrade the required version of pydantic in pyasic, but seems like you got it resolved.

@b-rowan
Copy link
Author

b-rowan commented Sep 3, 2025

Happy to update this branch as needed with changes, I'm just not fully familiar with the project so not sure what else is required to implement this adaptor.

@markoceri
Copy link
Collaborator

Happy to update this branch as needed with changes, I'm just not fully familiar with the project so not sure what else is required to implement this adaptor.

Starting from your excellent work, I've added everything necessary to fully integrate the PyAsic adapter into the rest of the application. Now that it has been fully incorporated, the only thing left is to give it a quick check to verify that everything is working as it should, so we can merge it into the main branch.

I don't have a miner and don't have the ability to test the functionality myself, but if someone is willing to, they can follow these steps:

  • Add a PyAsic-type miner controller (using RestAPI or CLI).
  • Add a miner (using RestAPI or CLI).
  • Associate the miner controller with the miner (using RestAPI or CLI).
  • Perform the start/stop/get status operations (only via RestAPI).

@b-rowan
Copy link
Author

b-rowan commented Sep 3, 2025

I don't have a miner and don't have the ability to test the functionality myself, but if someone is willing to, they can follow these steps:

I wrote a small simulator a while back for this exact thing, I'm not sure it keeps track of on/off state internally, but it should suffice for small tests.

https://github.com/UpstreamData/asic_simulator

@markoceri
Copy link
Collaborator

I wrote a small simulator a while back for this exact thing, I'm not sure it keeps track of on/off state internally, but it should suffice for small tests.

https://github.com/UpstreamData/asic_simulator

I'm trying it right now. For what it's needed, it does the job. Thanks, It is really a great solution, It will be very helpful! 👍

I'm fixing some issues with asynchronous function calls in mixed sync/async contexts. For simplicity, the adapter interface was designed to work in a synchronous context, and the solution of making async pyasic's calls in an Edge Mining sync context using asyncio.run() is good, but it doesn't work in contexts where an event loop already exists and we have no way to block the execution. (eg., if you want to manually interact with a miner via start/stop Fast API endpoints, which already creates an event loop).

The fastest option I think to implement (and sure also the least elegant) is to call the async functions via a dedicated function that splits the sync/async contexts and calls the async function in a new Thread allowing us to block execution, while waiting to convert all function calls to work in async contexts.

Something like:

def run_async_func(func: Callable[[], Coroutine[Any, Any, T]]) -> T:
    try:
        asyncio.get_running_loop()  # Triggers RuntimeError if no running event loop
        with ThreadPoolExecutor(1) as pool:
            return pool.submit(lambda: asyncio.run(func())).result()
    except RuntimeError:
        return asyncio.run(func())

What do you think? Do you have any suggestions that might be helpful?

@b-rowan
Copy link
Author

b-rowan commented Sep 8, 2025

I see no issues with this. The idea scenario IMO would be if the caller of the adaptor functions (one level above my understanding of what you are suggesting) could inspect the signatures of the functions in the adaptor and call it automatically (this is what FastAPI does).

Also nitpicking on the code in this comment, I believe you can use typing.Awaitable instead of the Callable which should remove the need to explicitly hint the Coroutine return type.

@markoceri
Copy link
Collaborator

I appreciate the point about using typing.Awaitable. My reasoning for sticking with Callable is to keep the code clean and well-typed.

Using Coroutine[Any, Any, T] as the function argument ensures direct compatibility with asyncio.run(), which specifically expects a coroutine object. This also helps the type checker accurately infer that the return type of run_async_func will be the same as the coroutine's return type, making the code more explicit and type-safe.

Anyway, thanks for the suggestions.

@Pipperix
Copy link

Hi! We tried testing the Pyasic adapter with a miner.
The miner is password protected and the execution throws an error as it fails to reach the miner/host.
Do you have any suggestions on how to proceed with this case?

@b-rowan b-rowan changed the base branch from main to dev September 18, 2025 14:19
@b-rowan
Copy link
Author

b-rowan commented Sep 18, 2025

Rebased onto dev, changed PR target, and cherry picked commits from feature-add-pyasic-miner-adapter.

@GitGab19 anything else that needs to be done?

@b-rowan b-rowan marked this pull request as ready for review September 18, 2025 14:20
@b-rowan
Copy link
Author

b-rowan commented Sep 18, 2025

Hi! We tried testing the Pyasic adapter with a miner. The miner is password protected and the execution throws an error as it fails to reach the miner/host. Do you have any suggestions on how to proceed with this case?

Usually miner.pwd should be set on the pyasic.AnyMiner instance, but not sure how to do that with regard to this software. Maybe @markoceri can provide some ideas?

@GitGab19
Copy link
Member

Rebased onto dev, changed PR target, and cherry picked commits from feature-add-pyasic-miner-adapter.

@GitGab19 anything else that needs to be done?

Awesome, thank you for doing it.

The other guys told me they wanted to test this PR with a real miner before merging it.

So I think we just need that and, together with a review, it should be good to go.

cc @markoceri @Pipperix

@markoceri
Copy link
Collaborator

Rebased onto dev, changed PR target, and cherry picked commits from feature-add-pyasic-miner-adapter.

@GitGab19 anything else that needs to be done?

Great! I wanted to ask you to merge the branch I created for testing with the one in your pull request, but you were quicker and already did it all yourself. 👍

Hi! We tried testing the Pyasic adapter with a miner. The miner is password protected and the execution throws an error as it fails to reach the miner/host. Do you have any suggestions on how to proceed with this case?

Usually miner.pwd should be set on the pyasic.AnyMiner instance, but not sure how to do that with regard to this software. Maybe @markoceri can provide some ideas?

You can do that adding the required authentication data to the MinerControllerPyASICConfig (eg. username and password) and give them to the init constructor of the PyASICMinerController in addition to the ip parameter, using PyASICMinerControllerAdapterFactory as the glue between the configuration and the instance, as you already did for the ip parameter.

The miner controller instance is created only once, the first time an instance of its ID is requested, and is then cached using the Adapter Service. Adapter Service takes care of retrieving the parameters saved in the configuration from the database (each type of miner adapter has its own) and passing them to the adapter constructor, which uses them to create the new instance. Any parameters required for the adapter to work can be added to the configuration and passed to the adapter constructor.

I hope I've been clear, if not I'll try to explain myself better. 🙂

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.

Implement pyasic adapter to control the miner

4 participants