Pyscript - new integration for easy and powerful Python scripting

would it be possible to use python-synology · PyPI in pyscript

I tried it with following code

import synology_dsm

from synology_dsm import SynologyDSM

@service
def shutdown():
    api = SynologyDSM(dsm_ip="xxx.xxx.xxx.22", dsm_port="5000", username="user", password="password", session="false")
    system = api.system
    system.shutdown()

but I get following error

Exception in <file.ds114_shutdown.shutdown> line 12: system.shutdown() ^ AttributeError: ‘str’ object has no attribute ‘get’

Please have a look at Importing

Hi guys, my a little heads-up about using pyscript in the docker version of HA.
I use my simple pyscript with selenium dependency defined in the requirements.txt and I observe strange behaviour about importing some classes and functions from the whole selenium library.
Sometimes I get object has no attribute ... for some specific import but the other stuff from the same library works fine. I figured out it’s needed to stop and rerun the whole docker, not just restart the HA instance in the docker.
Now I moved the piece of code with selenium import to pyscript_modules and the behaviour seems to be better.

Does anyone have a working example using !secret in the config? The help documentation refers to it working, but doesn’t show how the actual script would use it. I have the following in configuration.yaml:

pyscript:
  allow_all_imports: true
  apps:
    get_linked_image:
      - service_name: get_linked_image
        username: !secret link_user
        password: !secret link_pass

Those secrets exist and I’m using them in another section of my config. Then in the pyscript directory, I have a file named get_linked_image.py, which I dumbed down to see why it wasn’t working:

@service
def get_linked_image(url=None):
    log.error("in a script! (" + username + ")")

When I run this from the services tab, I see this in my log:

This error originated from a custom integration.

Logger: custom_components.pyscript.file.get_linked_image.get_linked_image
Source: custom_components/pyscript/eval.py:493
Integration: Pyscript Python scripting (documentation, issues)
First occurred: 3:31:44 PM (1 occurrences)
Last logged: 3:31:44 PM

Exception in <file.get_linked_image.get_linked_image > line 3: log.error("in a script! (" + username + ")") ^ NameError: name 'username' is not defined

How do I refer to these variables within my script, if not like this? I rebooted HA after adding them to the configuration.yaml.

Thanks!

1 Like

Hi

Why does this not work? The x != 5 or x !=6 check?

x = 6

if x == 0 or x == 1 or x == 3 or x == 4 :
    print("Should not print, and does not")

if x != 5 or x != 6:
   print("Should not print, but does")

x != 5 is True so the expression x != 5 or x != 6 evaluates to True. Because of short circuiting, x != 6 is not even evaluated, since its value doesn’t matter when the first expression is already True.

Ah, ok, so this fails just because today is day 6, and that is never checked for?

ChatGPT tried to tell me the same thing, i understood you better :stuck_out_tongue:

Thank you

Hi all, trying to make working a pyscript, but cannot understand why is not working. When i try to run service, i get following error:

Blockquote
Exception in <file.telegram_mark_msg_read.telegram_mark_msg_read> line 23: dialogs = await client.get_dialogs() ^ ConnectionError: Cannot send requests while disconnected

from telethon.sync import TelegramClient
from dotenv import dotenv_values
from telethon.sessions import StringSession


# Variabili
api_id="xxxxxxxxxx"
api_hash="xxxxxxxxxxxx
session_token="xxxxxxxxxxx"

def create_telegram_client(session_token, api_id, api_hash):
    client = TelegramClient(StringSession(session_token), api_id, api_hash)
    client.flood_sleep_threshold = 0
    client.start()
    return client



async def mark_all_as_read():
    dialogs = await client.get_dialogs()
    
    for dialog in dialogs:
        try:
            #print(f"NAME:{dialog.name}, USERNAME: {dialog.entity.id}")
            await client.send_read_acknowledge(dialog.entity.id)
            print(f"Marked as read: {dialog.name}")
        except Exception as e:
            print(f"Error marking as read in {dialog.name}: {e}")

    await client.disconnect()
    print("Client disconnected")

@service
def telegram_mark_msg_read():
    create_telegram_client(session_token, api_id, api_hash)
    client.loop.run_until_complete(mark_all_as_read())


Any idea?
regards

Does anyone know if it is possible to evaluate a template is pyscript? Either with pyscript, a service call or an another custom integration.

I have a configuration for an app written in pyscript like shown below. Instead of defining state and sensor, I would rather use a template so that I can use ‘{{ states(‘sensor.effekt’) > ‘300’ }}’ in the configuration of the app and evaluate this template in code. It will be more flexible that way and I don’t have to define several helper binary sensors.

      scenes:
          scene1:
            name: scene.stue_stovsuge
            sensor: binary_sensor.stovsuge
            state: "on"
            TransitionTo: 10
            TransitionFrom: 10
            TransitionMotion: 10
            delay: 30
            lightlevel: 5000
          scene2:
            name: scene.stue_middag
            sensor: input_boolean.middagslys
            state: "on"
            TransitionTo: 2
            TransitionFrom: 10

Can you share the pyscript code as well? It isn’t clear to me where you’d want to have the template evaluated right now.
At first glance, you can always just get the state from sensor.effekt in pyscript and check if it’s larger than 300 (you may need to convert the state from a string to a float/integer though).

No, I don’ want to use sensor.effekt in code. Then it is hardcoded and doesn’t work well in an app where it should be configurable.

As the code works now, I can only test for identities that has two states, normally on and off. I wan’t to extend that with templates, but doubt it is possible. But wanted to hear if someone know of a solution.

The trigger for the sensor is set up in this code section:

        if "scenes" in self.cfg:
            for tr in self.cfg['scenes']:
                if "sensor" in self.cfg['scenes'][tr]:
                    @state_trigger(f"{self.cfg['scenes'][tr]['sensor']} != 'unavailable'", state_hold = 1)
                    def statechanged_trig(var_name=None):
                        task.unique(f"scenestyringstatechg_{self.cfg['id']}_{var_name}")
                        self.set_sceneparams()
                        self.statechanged()
                    registered_triggers.append(statechanged_trig)

and state is checked in this section:

                if ("sensor" in self.cfg['scenes'][tr]
                        and "state" in self.cfg['scenes'][tr] 
                        and state.get(f"{self.cfg['scenes'][tr]['sensor']}") == self.cfg['scenes'][tr]['state']):
                    
                    if "name" in self.cfg['scenes'][tr]:
                        s = self.cfg['scenes'][tr]['name']

My best guess in that case would be using an eval() python function, and I guess putting in the template in a python format instead of jinja2? I.e. sensor.effekt > 300 in the configuration. However I’m not sure still if that is fully the goal you want to reach.

I also don’t have experience using eval(), so I can’t promise it well work. Got the answer from here:

Trying out PyScript for some automations, and keep running into an odd issue, hoping someone here can point me in the right direction.

Basically trying to compare times in a function. But i keep getting an error :

            if (dt.now() - last_flash).total_seconds > 300:
                           ^
TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'StateVal'

Code snippet is below. I added a print to confirm that “last_flash” was truly a datetime object and it seems to be (i’m reading it directly from an input_datetime object). So where is pyscript getting the “StateVal” type from? I also tried comparing directly (dt.now() - input_datetime.last_office_flash) with the same result, this format was just an attempt to add more visibility for debug.

        last_flash = input_datetime.last_office_flash
        log.info("last flash = {}".format(last_flash))
        if (dt.now() - last_flash).total_seconds > 300:

If you do a log.info(type(last_flash)), what do you get?

It does indeed report that it is StateVal

class 'custom_components.pyscript.state.StateVal

I just don’t understand why? I even did a

log.info(type(input_datetime.last_office_flash))

And it also reports the type as StateVal, but shouldn’t it be a datetime since it’s literally directly reading an input_datetime from HASS?

(I’m not the best python coder so if this is just a stupid misunderstanding of how types work in python I apologize)

I would try doing something like below. Basically, you need to convert the string value to a date value. I don’t remember off the top of my head what the date format is that HA uses, but you can output that and then use the format strings (datetime — Basic date and time types — Python 3.12.2 documentation) to get it to parse correctly. This should at least get you going in the right direction.

last_flash = datetime.strptime(input_datetime.last_office_flash, [date format])
log.info(dt.now() - last_flash).total_seconds

All states come in as strings. I don’t know when the last time that pyscript was updated though.

1 Like

Ugh, thanks. It should have been obvious that everything would come in as a string but the “StateVal” was throwing me off.

1 Like

The last couple of days pyscript has stopped reacting to state changes for me. For example, I have this very simple trigger:

@state_trigger("sensor.kontor_bordsfjarr_action == 'single'")
def kontor_bordsfjarr(**kwargs):
    light.toggle(entity_id="light.kontor_taklampa")

I can see it getting initialized on startup:

2024-04-02 08:48:30.235 DEBUG (MainThread) [custom_components.pyscript.trigger] trigger file.kontor.kontor_bordsfjarr: watching vars {'sensor.kontor_bordsfjarr_action'}
2024-04-02 08:48:30.235 DEBUG (MainThread) [custom_components.pyscript.trigger] trigger file.kontor.kontor_bordsfjarr waiting for state change or event

And I can see the sensor entity change in Developer Tools, but pyscript is not reacting to it. My pyscript apps execute like normal upon startup (some of them have an empty @time_trigger decorator and I can see the results).

Anyone has any tips on how I can troubleshoot this?

Hi, did you manage to solve this? :slight_smile:

No. I gave up and took a different approach to solve my issue.