Skip to content

Commit 712692f

Browse files
committed
setting a state variable doesn't change attributes;
added state.get_attr() to get attributes; state.set() can set or overwrite attributes, or leave them unchanged
1 parent b5bd968 commit 712692f

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed

README.md

+18-5
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,12 @@ State variables have attributes that can be accessed by adding the name of the a
261261
them, so you will need to look at the State tab in the Developer Tools to see the available
262262
attributes.
263263

264-
In cases where you need to compute the name of the state variable dynamically, or you need to
265-
set the state attributes, you can use the built-in functions `state.get(name)` and
266-
`state.set(name, value, attr=None)`; see below.
264+
Starting in version 0.21, when you set a state variable, the existing attributes are not
265+
affected (they were previously removed).
266+
267+
In cases where you need to compute the name of the state variable dynamically, or you need
268+
to set or get the state attributes, you can use the built-in functions `state.get()`,
269+
`state.get_attr()` and `state.set()`; see below.
267270

268271
The function `state.names(domain=None)` returns a list of all state variable names (ie, `entity_id`s)
269272
of a domain. If `domain` is not specified, it returns all HASS state variable (entity) names.
@@ -582,14 +585,23 @@ set the attributes, which you can't do if you are directly assigning to the vari
582585

583586
`state.get(name)` returns the value of the state variable, or `None` if it doesn't exist
584587

588+
`state.get_attr(name)` returns a `dict` of attribute values for the state variable, or `None`
589+
if it doesn't exist
590+
585591
`state.names(domain=None)` returns a list of all state variable names (ie, `entity_id`s)
586592
of a domain. If `domain` is not specified, it returns all HASS state variable (`entity_id`)
587593
names.
588594

589-
`state.set(name, value, attr=None)` sets the state variable to the given value, with the optional attributes.
595+
`state.set(name, value, new_attributes=None, **kwargs)` sets the state variable to the given value,
596+
with the optional attributes. The optional 3rd argument, `new_attributes`, should be a `dict` and
597+
it will overwrite all the existing attributes if specified. If instead attributes are specified
598+
using keyword arguments, then other attributes will not be affected. If no optional arguments
599+
are provided, just the state variable value is set and the attributes are not changed. To clear
600+
the attributes, set `new_attributes={}`.
590601

591602
Note that in HASS, all state variable values are coerced into strings. For example, if a state variable
592603
has a numeric value, you might want to convert it to a numeric type (eg, using `int()` or `float()`).
604+
Attributes keep their native type.
593605

594606
#### Service Calls
595607

@@ -605,11 +617,12 @@ parameters as the event data.
605617

606618
#### Logging functions
607619

608-
Four logging functions are provided, with increasing levels of severity:
620+
Five logging functions are provided, with increasing levels of severity:
609621
- `log.debug(str)`
610622
- `log.info(str)`
611623
- `log.warning(str)`
612624
- `log.error(str)`
625+
- `print(str)` is the same as `log.debug(str)`; currently `print` doesn't support other arguments.
613626

614627
The [Logger](/integrations/logger/) component can be used to specify the logging level. Log messages
615628
below the configured level will not appear in the log. Each log message function uses a log name of

custom_components/pyscript/state.py

+23-11
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,16 @@ def notify_var_get(var_names):
8383

8484
def set(var_name, value, attributes=None, **kwargs):
8585
"""Set a state variable and optional attributes in hass."""
86-
if len(var_name.split(".")) != 2:
87-
_LOGGER.error(
88-
"invalid variable name %s (should be 'domain.entity')", var_name
89-
)
90-
return
91-
if attributes or kwargs:
92-
if attributes is None:
86+
if var_name.count(".") != 1:
87+
raise NameError(f"invalid name {var_name} (should be 'domain.entity')")
88+
if attributes is None:
89+
state_value = State.hass.states.get(var_name)
90+
if state_value:
91+
attributes = state_value.attributes
92+
else:
9393
attributes = {}
94+
if kwargs:
95+
attributes = attributes.copy()
9496
attributes.update(kwargs)
9597
_LOGGER.debug("setting %s = %s, attr = %s", var_name, value, attributes)
9698
State.hass.states.async_set(var_name, value, attributes)
@@ -107,14 +109,23 @@ def get(var_name):
107109
"""Get a state variable value or attribute from hass."""
108110
parts = var_name.split(".")
109111
if len(parts) != 2 and len(parts) != 3:
110-
return None
112+
raise NameError(f"invalid name {var_name} (should be 'domain.entity')")
111113
value = State.hass.states.get(f"{parts[0]}.{parts[1]}")
112114
if not value:
113115
return None
114116
if len(parts) == 2:
115117
return value.state
116118
return value.attributes.get(parts[2])
117119

120+
def get_attr(var_name):
121+
"""Return a dict of attributes for a state variable."""
122+
if var_name.count(".") != 1:
123+
raise NameError(f"invalid name {var_name} (should be 'domain.entity')")
124+
value = State.hass.states.get(var_name)
125+
if not value:
126+
return None
127+
return value.attributes.copy()
128+
118129
def completions(root):
119130
"""Return possible completions of state variables."""
120131
words = set()
@@ -140,15 +151,16 @@ def completions(root):
140151
words.add(name.entity_id)
141152
return words
142153

143-
async def entity_ids(domain=None):
144-
"""Implement entity_ids."""
154+
async def names(domain=None):
155+
"""Implement names, which returns all entity_ids."""
145156
return State.hass.states.async_entity_ids(domain)
146157

147158
def register_functions():
148159
"""Register state functions."""
149160
functions = {
150161
"state.get": State.get,
151162
"state.set": State.set,
152-
"state.names": State.entity_ids,
163+
"state.names": State.names,
164+
"state.get_attr": State.get_attr,
153165
}
154166
Handler.register(functions)

tests/custom_components/pyscript/test_unit_eval.py

+21
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,27 @@
176176
"pyscript.var1 = 1; pyscript.var2 = 2; set(state.names('pyscript'))",
177177
{"pyscript.var1", "pyscript.var2"},
178178
],
179+
[
180+
"""
181+
state.set("pyscript.var1", 100, attr1=1, attr2=3.5)
182+
chk1 = [pyscript.var1.attr1, pyscript.var1.attr2]
183+
pyscript.var1 += "xyz"
184+
chk2 = [pyscript.var1.attr1, pyscript.var1.attr2]
185+
state.set("pyscript.var1", 200, attr3 = 'abc')
186+
chk3 = [pyscript.var1.attr1, pyscript.var1.attr2, pyscript.var1.attr3]
187+
chk4 = state.get_attr("pyscript.var1")
188+
state.set("pyscript.var1", pyscript.var1, {})
189+
chk5 = state.get_attr("pyscript.var1")
190+
[chk1, chk2, chk3, chk4, chk5]
191+
""",
192+
[
193+
[1, 3.5],
194+
[1, 3.5],
195+
[1, 3.5, "abc"],
196+
{"attr1": 1, "attr2": 3.5, "attr3": "abc"},
197+
{},
198+
],
199+
],
179200
["eval('1+2')", 3],
180201
["x = 5; eval('2 * x')", 10],
181202
["x = 5; exec('x = 2 * x'); x", 10],

0 commit comments

Comments
 (0)