Skip to content

added hass_is_global config parameter #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions custom_components/pyscript/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.loader import bind_hass

from .const import CONF_ALLOW_ALL_IMPORTS, DOMAIN, FOLDER, LOGGER_PATH, SERVICE_JUPYTER_KERNEL_START
from .const import (
CONF_ALLOW_ALL_IMPORTS,
CONF_HASS_IS_GLOBAL,
DOMAIN,
FOLDER,
LOGGER_PATH,
SERVICE_JUPYTER_KERNEL_START,
)
from .eval import AstEval
from .event import Event
from .function import Function
Expand All @@ -31,7 +38,11 @@
_LOGGER = logging.getLogger(LOGGER_PATH)

PYSCRIPT_SCHEMA = vol.Schema(
{vol.Optional(CONF_ALLOW_ALL_IMPORTS, default=False): cv.boolean}, extra=vol.ALLOW_EXTRA,
{
vol.Optional(CONF_ALLOW_ALL_IMPORTS, default=False): cv.boolean,
vol.Optional(CONF_HASS_IS_GLOBAL, default=False): cv.boolean,
},
extra=vol.ALLOW_EXTRA,
)

CONFIG_SCHEMA = vol.Schema({DOMAIN: PYSCRIPT_SCHEMA}, extra=vol.ALLOW_EXTRA)
Expand Down
26 changes: 16 additions & 10 deletions custom_components/pyscript/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.core import callback

from .const import CONF_ALLOW_ALL_IMPORTS, DOMAIN
from .const import CONF_ALLOW_ALL_IMPORTS, CONF_HASS_IS_GLOBAL, DOMAIN

CONF_BOOL_ALL = {CONF_ALLOW_ALL_IMPORTS, CONF_HASS_IS_GLOBAL}

PYSCRIPT_SCHEMA = vol.Schema(
{vol.Optional(CONF_ALLOW_ALL_IMPORTS, default=False): bool}, extra=vol.ALLOW_EXTRA,
{
vol.Optional(CONF_ALLOW_ALL_IMPORTS, default=False): bool,
vol.Optional(CONF_HASS_IS_GLOBAL, default=False): bool,
},
extra=vol.ALLOW_EXTRA,
)


Expand All @@ -34,15 +40,17 @@ async def async_step_init(self, user_input: Dict[str, Any] = None) -> Dict[str,
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_ALLOW_ALL_IMPORTS, default=self.config_entry.data[CONF_ALLOW_ALL_IMPORTS],
): bool
vol.Optional(name, default=self.config_entry.data.get(name, False)): bool
for name in CONF_BOOL_ALL
},
extra=vol.ALLOW_EXTRA,
),
)

if user_input[CONF_ALLOW_ALL_IMPORTS] != self.config_entry.data[CONF_ALLOW_ALL_IMPORTS]:
if any(
name not in self.config_entry.data or user_input[name] != self.config_entry.data[name]
for name in CONF_BOOL_ALL
):
updated_data = self.config_entry.data.copy()
updated_data.update(user_input)
self.hass.config_entries.async_update_entry(entry=self.config_entry, data=updated_data)
Expand Down Expand Up @@ -108,15 +116,13 @@ async def async_step_import(self, import_config: Dict[str, Any] = None) -> Dict[
# Update values for all keys, excluding `allow_all_imports` for entries
# set up through the UI.
for key, val in import_config.items():
if entry.source == SOURCE_IMPORT or key != CONF_ALLOW_ALL_IMPORTS:
if entry.source == SOURCE_IMPORT or key not in CONF_BOOL_ALL:
updated_data[key] = val

# Remove values for all keys in entry.data that are not in the imported config,
# excluding `allow_all_imports` for entries set up through the UI.
for key in entry.data:
if (
entry.source == SOURCE_IMPORT or key != CONF_ALLOW_ALL_IMPORTS
) and key not in import_config:
if (entry.source == SOURCE_IMPORT or key not in CONF_BOOL_ALL) and key not in import_config:
updated_data.pop(key)

# Update and reload entry if data needs to be updated
Expand Down
1 change: 1 addition & 0 deletions custom_components/pyscript/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
FOLDER = "pyscript"

CONF_ALLOW_ALL_IMPORTS = "allow_all_imports"
CONF_HASS_IS_GLOBAL = "hass_is_global"

SERVICE_JUPYTER_KERNEL_START = "jupyter_kernel_start"

Expand Down
8 changes: 7 additions & 1 deletion custom_components/pyscript/global_ctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import os
from types import ModuleType

from .const import FOLDER, LOGGER_PATH
from .const import CONF_HASS_IS_GLOBAL, DOMAIN, FOLDER, LOGGER_PATH
from .eval import AstEval
from .function import Function
from .trigger import TrigInfo
Expand All @@ -26,6 +26,12 @@ def __init__(self, name, global_sym_table=None, manager=None, rel_import_path=No
self.auto_start = False
self.module = None
self.rel_import_path = rel_import_path
config_entry = Function.hass.data.get(DOMAIN, {})
if config_entry.data.get(CONF_HASS_IS_GLOBAL, False):
#
# expose hass as a global variable if configured
#
self.global_sym_table["hass"] = Function.hass

def trigger_register(self, func):
"""Register a trigger function; return True if start now."""
Expand Down
6 changes: 4 additions & 2 deletions custom_components/pyscript/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"title": "pyscript",
"description": "Once you have created an entry, refer to the [docs](https://hacs-pyscript.readthedocs.io/en/latest/) to learn how to create scripts and functions.",
"data": {
"allow_all_imports": "Allow All Imports?"
"allow_all_imports": "Allow All Imports?",
"hass_is_global": "Access hass as a global variable?"
}
}
},
Expand All @@ -20,7 +21,8 @@
"init": {
"title": "Update pyscript configuration",
"data": {
"allow_all_imports": "Allow All Imports?"
"allow_all_imports": "Allow All Imports?",
"hass_is_global": "Access hass as a global variable?"
}
},
"no_ui_configuration_allowed": {
Expand Down
6 changes: 4 additions & 2 deletions custom_components/pyscript/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"title": "pyscript",
"description": "Once you have created an entry, refer to the [docs](https://hacs-pyscript.readthedocs.io/en/latest/) to learn how to create scripts and functions.",
"data": {
"allow_all_imports": "Allow All Imports?"
"allow_all_imports": "Allow All Imports?",
"hass_is_global": "Access hass as a global variable?"
}
}
},
Expand All @@ -20,7 +21,8 @@
"init": {
"title": "Update pyscript configuration",
"data": {
"allow_all_imports": "Allow All Imports?"
"allow_all_imports": "Allow All Imports?",
"hass_is_global": "Access hass as a global variable?"
}
},
"no_ui_configuration_allowed": {
Expand Down
17 changes: 11 additions & 6 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
Configuration
=============

- Go to the Integrations menu in the Home Assistant Configuration UI and add
``Pyscript Python scripting`` from there, or add ``pyscript:`` to
``<config>/configuration.yaml``; pyscript has one optional configuration
parameter that allows any python package to be imported if set, eg:
- Pyscript can be configured using the UI, or via yaml. To use the UI, go to the
Configuration -> Integrations page and selection "+" to add ``Pyscript Python scripting``.
After that, you can change the settings anytime by selecting Options under Pyscript
in the Configuration page.

Alternatively, for yaml configuration, add ``pyscript:`` to ``<config>/configuration.yaml``.
Pyscript has two optional configuration parameters that allow any python package to be
imported and exposes the ``hass`` variable as a global (both options default to ``false``):

.. code:: yaml

pyscript:
allow_all_imports: true
hass_is_global: true

- Add files with a suffix of ``.py`` in the folder ``<config>/pyscript``.
- Restart HASS.
- Whenever you change a script file, make a ``reload`` service call to ``pyscript``.
- Restart HASS after installing pyscript.
- Whenever you change a script file or configuration, make a ``reload`` service call to ``pyscript``.
- Watch the HASS log for ``pyscript`` errors and logger output from your scripts.
80 changes: 74 additions & 6 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,34 @@ Reference
Configuration
-------------

Pyscript has one optional configuration setting that allows any Python package to be imported
if set, eg:
Pyscript can be configured using the UI, or via yaml. To use the UI, go to the
Configuration -> Integrations page and selection "+" to add ``Pyscript Python scripting``.
After that, you can change the settings anytime by selecting Options under Pyscript
in the Configuration page.

Alternatively, for yaml configuration, add ``pyscript:`` to ``<config>/configuration.yaml``.
You can't mix these two methods - your initial choice determines how you should update
these settings later. If you want to switch configuration methods you will need to
uninstall and reinstall pyscript.

Pyscript has two optional configuration parameters that allow any python package to be
imported and exposes the ``hass`` variable as a global (both options default to ``false``).
In `<config>/configuration.yaml``:

.. code:: yaml

pyscript:
allow_all_imports: true
hass_is_global: true

The settings and behavior of your code can be controlled by additional user-defined yaml
configuration settings. If you configured pyscript using the UI flow, you can still
add additional configuration settings via yaml. Since they are free-form (no fixed
schema) there is no UI configuration available for these additional settings.

The settings and behavior of your code can be controlled by additional user-defined configuration
settings. All the pyscript configuration settings are available via the variable
``pyscript.config`` (see `this section <#accessing-yaml-configuration>`__). The recommended
structure is to have entries for each application you write stored under an ``apps`` entry.
All the pyscript configuration settings are available via the variable ``pyscript.config``
(see `this section <#accessing-yaml-configuration>`__). The recommended structure is
to have entries for each application you write stored under an ``apps`` entry.
For example, applications ``my_app1`` and ``my_app2`` would be configured as:

.. code:: yaml
Expand Down Expand Up @@ -1078,6 +1094,58 @@ triggers and application logic, eg:

Validating the configuration can be done either manually or with the ``voluptuous`` package.

Access to Hass
^^^^^^^^^^^^^^

If the ``hass_is_global`` configuration setting is set (default is off), then the variable ``hass``
is available as a global variable in all pyscript contexts. That provides significant flexiblity
in accessing HASS internals for cases where pyscript doesn't provide some binding or access.

Ideally you should only use ``hass`` for read-only access. However, you do need a good understanding
of ``hass`` internals and objects if you try to call functions or update anything. With great power
comes great responsibility!

For example, you can access configuration settings like ``hass.config.latitude`` or ``hass.config.time_zone``.

You can use ``hass`` to compute sunrise and sunset times using the same method HASS does, eg:

.. code:: python

import homeassistant.helpers.sun as sun
import datetime

location = sun.get_astral_location(hass)
sunrise = location.sunrise(datetime.datetime.today()).replace(tzinfo=None)
sunset = location.sunset(datetime.datetime.today()).replace(tzinfo=None)
print(f"today sunrise = {sunrise}, sunset = {sunset}")

Here's another method that uses the installed version of ``astral`` directly, rather than the HASS
helper function. It's a bit more crytpic since it's a very old version of ``astral``, but you can
see how the HASS configuration values are used:

.. code:: python

import astral
import datetime

here = astral.Location(
(
"",
"",
hass.config.latitude,
hass.config.longitude,
str(hass.config.time_zone),
hass.config.elevation,
)
)
sunrise = here.sunrise(datetime.datetime.today()).replace(tzinfo=None)
sunset = here.sunset(datetime.datetime.today()).replace(tzinfo=None)
print(f"today sunrise = {sunrise}, sunset = {sunset}")

If there are particular HASS internals that you think many pyscript users would find useful,
consider making a feature request or PR so it becomes a built-in feature in pyscript, rather
than requiring users to always have to delve into ``hass``.

Avoiding Event Loop I/O
^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
Loading