Skip to content

Commit 666e3fc

Browse files
committed
added tests for new virtual methods on entity_ids feature; #64
1 parent f32abc2 commit 666e3fc

File tree

3 files changed

+74
-9
lines changed

3 files changed

+74
-9
lines changed

docs/new_features.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ The new features since 0.32 in master include:
4444
still available and will be removed in some future release.
4545
- Entities ``DOMAIN.ENTITY`` now support a virtual method ``SERVICE`` (eg, ``DOMAIN.ENTITY.SERVICE()``)
4646
that calls a service ``DOMAIN.SERVICE`` for any service that has an ``entity_id`` parameter.
47-
Proposed @dlashua (#64).
47+
Proposed by @dlashua (#64).
4848

4949
The bug fixes since 0.32 in master include:
5050

docs/reference.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ For any state variable ``DOMAIN.ENTITY``, any services registered by ``DOMAIN``,
148148
DOMAIN.ENTITY.SERVICE(other_param=123)
149149
150150
In the case the service has only one other parameter in addition to ``entity_id``, a further
151-
shorthand is that the method can be called with just a positional, rather than keyword parameter.
151+
shorthand is that the method can be called with just a positional, rather than keyword, argument.
152152
So if the service only takes two parameters, ``entity_id`` and ``other_param``, this additional
153153
form is equivalent to each of the above statements:
154154

@@ -158,7 +158,7 @@ form is equivalent to each of the above statements:
158158
159159
Here's an example using ``input_number``, assuming it has been configured to create an entity
160160
``input_number.test``. These statements all do the same thing (and the last one works because
161-
``set_value`` only takes on other parameter):
161+
``set_value`` only takes one other parameter):
162162

163163
.. code:: python
164164
@@ -173,8 +173,6 @@ Two additional virtual attribute values are available when you use a variable di
173173
- ``last_changed`` is the last UTC time the state value was changed (not the attributes)
174174
- ``last_updated`` is the last UTC time the state entity was updated
175175

176-
Note that these two values take precedence over any entity attributes that have the same name. If an
177-
entity has attributes with those names and you need to access them, use ``state.getattr(name)``.
178176
If you need to compute how many seconds ago the ``binary_sensor.test1`` state changed, you could
179177
do this:
180178

@@ -185,6 +183,10 @@ do this:
185183
186184
num_seconds_ago = (dt.now(tz=timezone.utc) - binary_sensor.test1.last_changed).total_seconds()
187185
186+
Note that these virtual attributes and methods take precedence over any entity attributes that
187+
have the same name. If an entity has attributes with those names and you need to access them,
188+
use ``state.getattr(name)``.
189+
188190
Calling services
189191
----------------
190192

tests/test_function.py

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from datetime import datetime as dt
66
import time
77

8-
from custom_components.pyscript.const import CONF_ALLOW_ALL_IMPORTS, DOMAIN
8+
from custom_components.pyscript.const import CONF_ALLOW_ALL_IMPORTS, CONF_HASS_IS_GLOBAL, DOMAIN
99
from custom_components.pyscript.function import Function
1010
import custom_components.pyscript.trigger as trigger
1111
import pytest
@@ -98,23 +98,25 @@ async def test_service_completions(root, expected, hass, services): # pylint: d
9898
assert words == expected
9999

100100

101-
async def setup_script(hass, notify_q, now, source):
101+
async def setup_script(hass, notify_q, now, source, config=None):
102102
"""Initialize and load the given pyscript."""
103103
scripts = [
104104
"/some/config/dir/pyscripts/hello.py",
105105
]
106106

107+
if not config:
108+
config = {DOMAIN: {CONF_ALLOW_ALL_IMPORTS: True}}
107109
with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch(
108110
"custom_components.pyscript.glob.iglob", return_value=scripts
109111
), patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source), create=True,), patch(
110112
"custom_components.pyscript.trigger.dt_now", return_value=now
111113
), patch(
112-
"homeassistant.config.load_yaml_config_file", return_value={DOMAIN: {CONF_ALLOW_ALL_IMPORTS: True}}
114+
"homeassistant.config.load_yaml_config_file", return_value=config
113115
), patch(
114116
"custom_components.pyscript.process_all_requirements",
115117
return_value={"/some/config/dir/pyscript/requirements.txt": ["pytube==2.0.1", "pykakasi==2.0.1"]},
116118
):
117-
assert await async_setup_component(hass, "pyscript", {DOMAIN: {CONF_ALLOW_ALL_IMPORTS: True}})
119+
assert await async_setup_component(hass, "pyscript", config)
118120

119121
#
120122
# I'm not sure how to run the mock all the time, so just force the dt_now()
@@ -742,3 +744,64 @@ def func_startup_sync2(trigger_type=None, var_name=None):
742744
hass.states.async_set("pyscript.fstartup0", 0)
743745
hass.states.async_set("pyscript.fstartup0", 1)
744746
assert literal_eval(await wait_until_done(notify_q)) == [0, "state", "pyscript.fstartup0"]
747+
748+
749+
async def test_state_methods(hass, caplog):
750+
"""Test state methods that call services."""
751+
notify_q = asyncio.Queue(0)
752+
753+
await setup_script(
754+
hass,
755+
notify_q,
756+
[dt(2020, 7, 1, 12, 0, 0, 0)],
757+
"""
758+
759+
seq_num = 0
760+
761+
@time_trigger("startup")
762+
def func_startup():
763+
pyscript.var1 = 10
764+
pyscript.var1.set(20)
765+
pyscript.var1.set(value=30)
766+
pyscript.var1.incr()
767+
pyscript.var1.incr()
768+
pyscript.var1.set_add(val1=10, val2=40)
769+
#
770+
# this will generate an exception
771+
#
772+
pyscript.var1.set_add(10)
773+
774+
@service
775+
def set(entity_id=None, value=None):
776+
global seq_num
777+
778+
seq_num += 1
779+
state.set(entity_id, value)
780+
pyscript.done = [seq_num, entity_id, state.get(entity_id)]
781+
782+
@service
783+
def incr(entity_id=None):
784+
global seq_num
785+
786+
seq_num += 1
787+
state.set(entity_id, int(state.get(entity_id)) + 1)
788+
pyscript.done = [seq_num, entity_id, state.get(entity_id)]
789+
790+
@service
791+
def set_add(entity_id=None, val1=None, val2=None):
792+
global seq_num
793+
794+
seq_num += 1
795+
state.set(entity_id, val1 + val2)
796+
pyscript.done = [seq_num, entity_id, state.get(entity_id), type(hass).__name__]
797+
798+
""",
799+
config={DOMAIN: {CONF_ALLOW_ALL_IMPORTS: True, CONF_HASS_IS_GLOBAL: True}},
800+
)
801+
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
802+
assert literal_eval(await wait_until_done(notify_q)) == [1, "pyscript.var1", "20"]
803+
assert literal_eval(await wait_until_done(notify_q)) == [2, "pyscript.var1", "30"]
804+
assert literal_eval(await wait_until_done(notify_q)) == [3, "pyscript.var1", "31"]
805+
assert literal_eval(await wait_until_done(notify_q)) == [4, "pyscript.var1", "32"]
806+
assert literal_eval(await wait_until_done(notify_q)) == [5, "pyscript.var1", "50", "HomeAssistant"]
807+
assert "TypeError: service pyscript.set_add takes no positional arguments" in caplog.text

0 commit comments

Comments
 (0)