Pyscript - new integration for easy and powerful Python scripting

Hi Craig, after installing the last version of pyscript, and moving all my scripts to the apps folder, reload doesn’t work anymore.

The situation is like this: I have all the pyscript apps running perfectly, then I make a reload (from the integration page or from calling the service with global_ctx: ‘*’ is the same) and every trigger stops working.
Note that I see the reload messages in the log:
2021-02-21 02:05:32 INFO (MainThread) [custom_components.pyscript.global_ctx] Reloaded /config/pyscript/apps/motion_lights.py
but no trigger is working after that, and I have to restart Home Assistant.

This was working perfectly before.
Thanks for your help!

Fabio

Yes, it’s a bug - thanks for reporting it!

I just fixed it with this commit, so you can test the fix by installing the master git version. The fix will be in the next version, which should get released in the next week.

Thanks Craig, I just tried the fix: half resolved ! :slight_smile:

The reload using the service with global_ctx: ‘*’ is OK.
But the reload from the integrations page still kills the triggers (as before the log report the reload of the apps), and you cannot recover functionality without restarting HA; also using the service doesn’t help.

Thanks again for pyscript: I ported all my apps from appdaemon, which is indeed a good application, but for my use I prefer less moving parts, and pyscripy being part of HA is much better for me.
Also syntax result a little bit more elegant in my opinion.

I agree with this and thanks, @craigb. I have also noticed that pyscript is faster than appdaemon on my system. It could take appdaemon 0.2 - 0.5 second to react on an event or state change, but with pyscript it is instantaneous. I hope it stays that way when all of my apps are moved to pyscript :slight_smile:

Oops - as you can guess I don’t use that method, especially now there is auto-reload. It should be fixed now (I still need to add some tests):

@Watson and @stigvi - thanks for the feedback!

I’m a professional programmer, but not on a completly different system (iseries)
But, this makes, I like programmer much more in python (that I’m still learning) than working with yaml rules…
When I discovered pyscript, I was very pleased. And, as I see here also, it’s just getting better.
I’ve found https://hacs-pyscript.readthedocs.io/en/latest/index.html.
But, what I still miss, is a more dedicated community area, for pyscript. For now, I only find here and there a bit pyscript code… Or a separate category here to begin with. I 've found an appdaemon category,… but no pyscript…
As I, and others as I see here, going to convert my rules to pyscript, and write new code in pyscript,
a dedicated place for pyscript would be very welcome…
Hopefully, this can be made possible…
In the mean time, thanks for your work!

1 Like

thats because pyscript is relative new and small and appdaemon is over 6 years old and has thousands of users :wink:

Thanks for this awesome project.
I am relatively new to home assistant and struggled to play local playlists. With pyscript and 2 afternoons it works like a charme.
I am using the media_player service
media_player.play_media(entity_id=mydevice, media_content_type="music", media_content_id=songs[song])

to get the result in yaml (from their example):

entity_id: media_player.chromecast
service: media_player.play_media
data:
  media_content_type: music
  media_content_id: "https://fake-home-assistant.io.stream/aac"
  extra:
    thumb: "https://brands.home-assistant.io/_/homeassistant/logo.png"
    title: HomeAssitantRadio  

which works fine. But how can I put the ‘extra’ data into the pyscript call?
Just adding extra="title: sometitle" or extra="title= sometitle" as parameter results in a
MultipleInvalid: expected dict for dictionary value @ data[‘extra’]

Any thoughts?

Try extra={‘title’:‘sometitle’}

1 Like

Thank you so much. works fine
Cheers

Has anyone implemented a TCP socket client with Pyscript that doesn’t block? I’m trying to update/get updates from a bose system using TCP and keeping a TCP connection open while doing so

You can use asyncio.open_connection to create an async client socket which runs safely inside pyscript:

import asyncio

def tcp_echo_client(message):
    reader, writer = asyncio.open_connection('127.0.0.1', 8888)

    writer.write(message.encode())
    writer.drain()

    data = reader.readline()

    writer.close()
    writer.wait_closed()

This function opens a client connection, sends and receives one message, then closes the connection. If you want to have the connection stay open you could move the connection code into a @time_trigger('@startup') function, eg:

import asyncio

Reader, Writer = None, None

@time_trigger('startup')
def do_client_connection():
    global Reader, Writer
    Reader, Writer = asyncio.open_connection('127.0.0.1', 8956)

def client_send(message):
    if not Writer:
       raise("Client is not connected")
    Writer.write(message.encode())
    Writer.drain()
    return Reader.readline().decode()

Then you can call client_send() and it will send the message and return the reply. This assumes the protocol is line-oriented; you could call Reader.read() instead if you want to read bytes instead of expecting a newline with Reader.readline().

To test the code above, you can create a server with nc before you run the code:

nc -l 8956

When you call client_send("hello\n") you should see the "hello" printed by nc. Then whatever you type back at nc will be returned by client_send().

1 Like

Hi,

I wonder if you could help me a bit? I’m trying to implement a FSM using transitions library and use that with pyscript. Transitions is heavily based on callbacks, in which case I think I’m supposed to decorate the callbacks using @pyscript_compile.

That works up to a point, but I would also like to make changes to HA from those callbacks (or by any means). I’d like to set an input_select to a certain option in certain states.

So, my question is; how could I get a handle on home assistant elements from a compiled callback? If that is impossible, is there a better design?

If it’s helpful in making my idea more understandable, I could provide a small code sample of what I wish to achieve.

And thanks for making pyscript! It’s beautiful!

1 Like

Am I the only one that first read Flying Spaghetti Monster? :wink:

Yes, if a package needs callbacks (unless it explicitly supports async callbacks), then you need to provide those using @pyscript_compile.

That means you can’t add any pyscript code inside those compiled functions, since pyscript code is async.

Here are some ideas:

  1. Your compiled functions can access hass, provided hass_is_global is set. You’ll only be able to call regular hass functions, not async ones. Here’s an example
@pyscript_compile
def get_entity(entity):
    return hass.states.get(entity).state

pyscript.var0 = "hello"
get_entity("pyscript.var0")
  1. You could create an async queue that allows your compiled python functions to send messages to a handler written in pyscript that could then process the message and do whatever you wish. The trick this exploits is that an async queue offers a put_nowait method that is a regular function that doesn’t block. Example:
import asyncio

mesg_q = asyncio.Queue(0)

@pyscript_compile
def send_message(mesg):
    mesg_q.put_nowait(mesg)
    
@time_trigger("startup")
def process_messages():
    task.unique("process_messages")
    while True:
        mesg = mesg_q.get()
        log.info(f"received mesg {mesg}")

# test code
send_message("message #1")
send_message("message #2")
send_message("message #3")

mesg could instead be a dict with multiple entries so you could specify a rich set of actions that the handling task executes any time a state machine callback tells it. The handling task of course has full access to pyscript features.

Brilliant, thank you again @craigb. I was able to solve the issue by switching to an async version of the state machine library, namely AsyncMachine, which is something that transitions library provides out of the box. That and few awaits sprinkled here and there, and I got my state machine working nicely along pyscript!

Is it possible to write about what you are doing in another thread?

Why does this fail in pyscript?

from enum import Enum

class Status(Enum):
    OPPE = 1
    HALVVEIS = 2
    LUKKET = 3
    UKJENT = 4
    MANUELL = 5

The error message is:

2021-03-15 07:35:40 ERROR (MainThread) [custom_components.pyscript.apps.gardiner] Exception in </config/pyscript/apps/gardiner.py> line 9:
        MANUELL = 5
        ^
AttributeError: 'dict' object has no attribute '_member_names'

Did you direct that question to me?

Yes :slight_smile: Sorry for pressing the wrong reply button…
What you are doing looks interesting and I think more than me would like to know more. And I think more will see it if it is on a thread of its own