Open KazukiOmata opened 3 months ago
I want to use pyautogui when I have it keydown, but after adding just a few lines, the exclamation mark appears all the time from the icon. The readme says it's because it hasn't finished loading, but I'm wondering if it could be due to some other issue. I would like to read the error log for that.
import requests
from streamdeck_sdk import (
StreamDeck,
Action,
events_received_objs,
events_sent_objs,
image_bytes_to_base64,
logger,
)
import settings
import pyautogui
class LoremFlickrError(Exception):
pass
def get_image_from_lorem_flickr(
category: str,
union_type: str = "or",
grayscale_flag: bool = False,
timeout: float = 5,
) -> str:
url = settings.LOREM_FLICKR_URL
if grayscale_flag:
url += "/g"
url += f"/{settings.IMAGE_SIZE}/{settings.IMAGE_SIZE}/{category}"
if union_type == "and":
url += "/all"
logger.info(f"{url=}")
response = requests.get(url, stream=True, timeout=timeout)
if not response.ok:
raise LoremFlickrError(f"Bad response. Status code: {response.status_code}")
image_binary = response.content
image_mime = response.headers["Content-Type"]
image_base64 = image_bytes_to_base64(obj=image_binary, image_mime=image_mime)
return image_base64
class SetKeyImage(Action):
UUID = "com.ggusev.loremflickr.setkeyimage"
def on_key_down(self, obj: events_received_objs.KeyDown):
screenWidth, screenHeight = pyautogui.size()
category = obj.payload.settings.get("category", screenWidth)
union_type = obj.payload.settings.get("union_type", "or")
grayscale_flag = obj.payload.settings.get("grayscale_flag", False)
logger.info(f"{category=}, {union_type=}, {grayscale_flag=}")
try:
image_base64 = get_image_from_lorem_flickr(
category=category,
union_type=union_type,
grayscale_flag=grayscale_flag,
)
except Exception as err: # noqa
logger.error(str(err))
self.show_alert(context=obj.context)
return
self.set_image(
context=obj.context,
payload=events_sent_objs.SetImagePayload(
image=image_base64,
target=0,
state=obj.payload.state,
)
)
if __name__ == '__main__':
StreamDeck(
actions=[
SetKeyImage(),
],
log_file=settings.LOG_FILE_PATH,
log_level=settings.LOG_LEVEL,
log_backup_count=1,
).run()
print("hello")
@KazukiOmata Hello, the log files is located in the directory of the plugin installed in Stream Deck.
Example for Mac OS: /Users/gri-gus/Library/Application Support/com.elgato.StreamDeck/Plugins/com.ggusev.loremflickr.sdPlugin/logs/
init.log
: log of the initialization of the project (creating venv, installing dependencies, etc.).
com.ggusev.loremflickr.sdPlugin.log
: log of the plugin and the libraries that are used in it. If this file is missing, then most likely the initialization has not yet completed or there are errors indicated in the log.
I also advise you to double-check whether you have added pyautogui
and its dependencies to the file requirements.txt
@gri-gus
Hello,
thank you for answering. I found the log file init.log
.
maybe, com.ggusev.loremflickr.sdPlugin.log
was not generated yet.
I checked the contents of venv and noticed that pyatuogui was not pip installed.
I fixed requirements.txt
and it works fine.
thank you very much
@gri-gus Am I correct that init.py is only called the first time I load the plugin?
and, Is it possible to turn this python into an executable file? I would like to be able to use the streamdeck plugin on machines that do not have python natively installed, such as M1 macs.
@KazukiOmata
Stream Deck does not have the ability to run Python files, but it does have the ability to run .bat
and .sh
files.
But it all starts with the manifest.json
file, which contains "CodePathMac": "run.sh"
and "CodePathWin": "run.bat"
. These are scripts that are run depending on the system.
Files run.sh
for MacOS or run.bat
for Windows: startup scripts, entry points. They set environment variables, check whether Python is installed (if not, an error window pops up), and run the init.py
script. Based on the result from init.py
, if everything is fine, then the main.py
file is launched and the plugin is launched, and if not, then an error window pops up. This happens every time you restart the Stream Deck application and when you reinstall/update the plugin.
What is the init.py
file? This is a script that is responsible for the virtual environment and dependencies. Why can't we immediately add venv
to the built version of the plugin? Because a user with a system/hardware different from the one on which venv
was made may encounter compatibility problems. Therefore, for Python, everyone should have their own virtual environment for each project. The init.py
file is responsible for creating the virtual environment, installing dependencies from requirements.txt
, and checking that everything is installed correctly. It also runs every time you restart the Stream Deck application and when you reinstall/update the plugin. But if the virtual environment has already been created and the dependencies are installed, then init.py
simply checks that everything is installed correctly.
Later, the main.py
file comes into play, which contains the plugin logic.
Not entirely true. It is called every time the Stream Deck application is restarted and when the plugin is reinstalled/updated, but if the virtual environment has already been created and the dependencies are installed, then init.py
simply checks that everything is installed correctly.
In theory it is possible, but I have not done this. In this case, if everything works out, you will need to specify it in manifest.json
and build the plugin. But other users may encounter Python libs compatibility issues.
Hello
if main.py call send_to_property_inspector() like below main.py
self.send_to_property_inspector(
action=self.UUID,
context=obj.context,
payload=_payload
)
How Property inspector does receive payload data? your sample project doesn't contain like that code?
@KazukiOmata
Let's look at an example of how the self.send_to_property_inspector
method works.
Let's look at the documentation from Elgato:
Here is the object sent from the plugin when calling self.send_to_property_inspector
: click
Here is the resulting object in the Property inspector when calling self.send_to_property_inspector
: click
Here is the method source code for the self.send_to_property_inspector method:
def send_to_property_inspector(
self,
action: str,
context: str,
payload: dict
):
message = events_sent_objs.SendToPropertyInspector(
action=action,
context=context,
payload=payload
)
self.send(message)
As we can see, it accepts function parameters and transfers them to the object events_sent_objs.SendToPropertyInspector:
class SendToPropertyInspector(BaseModel):
action: str
context: str
payload: dict
event: str = "sendToPropertyInspector"
Next in the method self.send the pydantic object is converted to json and sent to Property Inspector.
payload
?It's any dict
you want. But there is a condition, it must be convertible to json.
To answer this question, you need to look at the source code streamdeck-javascript-sdk. As I understand, in their sdk there is a method onSendToPropertyInspector and most likely it should be used like this:
$PI.onSendToPropertyInspector("com.ggusev.keyboard.write", jsn => {
payload = jsn.payload; // I'm not sure about this, you need to test it
...
});
Instead of "com.ggusev.keyboard.write"
you need to substitute the name of your action.
According to settings.py, it looks like the log file will be output, Where can i find the exported log file? mac os M1 max Sonoma 14.3.1