How do you force a template to render as a quoted string in an automation? I have the following automation that updates my lock codes when an OTP sensor changes:
alias: OTP Update Lock Codes
trigger:
- platform: state
entity_id:
- sensor.otp_code
action:
- service: zwave_js.invoke_cc_api
target:
entity_id: lock.front_door
data:
command_class: 99
method_name: setMany
parameters:
- - userId: 2
userIdStatus: 1
userCode: "{{ states('sensor.otp_code') }}"
However, when the automation runs, the userCode
renders as an unquoted string, which gets interpreted as a number by Z-Wave JS:
params:
domain: zwave_js
service: invoke_cc_api
service_data:
command_class: 99
method_name: setMany
parameters:
- - userId: 2
userIdStatus: 1
userCode: 123456
entity_id:
- lock.front_door
target:
entity_id:
- lock.front_door
running_script: false
This is no good because the command requires userCode
to be a string:
userId: number;
userIdStatus: UserIDStatus.Available;
userCode?: undefined;
}
| {
userId: number;
userIdStatus: Exclude<
UserIDStatus,
UserIDStatus.Available | UserIDStatus.StatusNotAvailable
>;
userCode: string | Buffer;
};
@CCCommand(UserCodeCommand.Set)
@useSupervision()
export class UserCodeCCSet extends UserCodeCC {
public constructor(
host: ZWaveHost,
options:
| CommandClassDeserializationOptions
| (CCCommandOptions & UserCodeCCSetOptions),
I can execute the command manually in the Developer console which works as expected:
service: zwave_js.invoke_cc_api
target:
entity_id: lock.front_door
data:
command_class: 99
method_name: setMany
parameters:
- - userId: 1
userIdStatus: 1
userCode: "123456"
I’ve tried several things to try to force it to render correctly as userCode: "123456"
, none of which work.
All of these render as an unquoted number:
userCode: "{{ states('sensor.otp_code') | string }}"
userCode: "{{ ('0' + states('sensor.otp_code'))[1:] }}"
userCode: >-
{% set code = states('sensor.otp_code') %}
{{ code }}
userCode: >-
{{
states('sensor.otp_code')
}}
Renders with too many quotes:
userCode: '{{ states('sensor.otp_code') | tojson }}'
> '"123456"'
mekaneck
(Rick Auch)
April 15, 2024, 3:50am
2
Can you concatenate quotes onto it?
userCode: "{{ '\'' ~ states('sensor.otp_code') ~ '\'' }}"
The backslash escapes the character that comes next, if you’re wondering how that template works.
Unfortunately, once I save the automation, your code gets changed to this representation:
userCode: "{{ \"'\" ~ states('sensor.otp_code') ~ \"'\" }}"
Furthermore, the run details look like this, which includes way too many quotes:
params:
domain: zwave_js
service: invoke_cc_api
service_data:
command_class: 99
method_name: setMany
parameters:
- - userId: 2
userIdStatus: 1
userCode: '''900048'''
entity_id:
- lock.front_door
target:
entity_id:
- lock.front_door
running_script: false
freshcoast
(Z-Wave AI Bot)
April 15, 2024, 4:30am
4
How about converting the string to a Buffer:
alias: OTP Update Lock Codes
trigger:
- platform: state
entity_id:
- sensor.otp_code
action:
- service: zwave_js.invoke_cc_api
target:
entity_id: lock.front_door
data:
command_class: 99
method_name: setMany
parameters:
- - userId: 2
userIdStatus: 1
userCode:
type: Buffer
data: "{{ states('sensor.otp_code') | map('ord') | list }}"
If you are only setting a single user code just use the dedicated service call.
action:
- service: zwave_js.set_lock_usercode
target:
entity_id: lock.front_door
data:
code_slot: 2
usercode: "{{ states('sensor.otp_code') }}"
Using the buffer results in a run that looks like this:
params:
domain: zwave_js
service: invoke_cc_api
service_data:
command_class: 99
method_name: setMany
parameters:
- - userId: 2
userIdStatus: 1
userCode:
type: Buffer
data:
- 55
- 56
- 49
- 56
- 50
- 48
entity_id:
- lock.front_door
target:
entity_id:
- lock.front_door
running_script: false
But I’m still getting an error message in the Z-wave log file: Z-Wave error ZWaveError: codes has the wrong type (ZW0322)
I’m planning on setting multiple user codes but am validating that I can get a single code to work first.
freshcoast
(Z-Wave AI Bot)
April 15, 2024, 4:49am
6
As an experiment, what happens if you use set
instead of setMany
?
method_name: set
parameters:
- 2
- 1
- type: Buffer
data: "{{ states('sensor.otp_code') }}"
I get Z-Wave error ZWaveError: userCode has the wrong type
tom_l
April 15, 2024, 4:57am
8
Try:
userCode: >
{{ states('sensor.otp_code') | tojson }}
1 Like
freshcoast
(Z-Wave AI Bot)
April 15, 2024, 4:58am
9
Any chance you didn’t enter it as-is? It was confirmed working here: https://community.home-assistant.io/t/zipato-wintop-keypad-rfid-tag-reader/682586/5?u=freshcoast
I don’t think setMany
will work using a Buffer, doesn’t look like the z-wave js server can decode a buffer within an object, only an array.
I’m pretty sure, but I’ll verify this tomorrow. My lock is now unresponsive and won’t reconnect to HA.
I’ll also try this tomorrow when I get my lock reconnected
freshcoast
(Z-Wave AI Bot)
April 15, 2024, 7:19am
11
Not sure I would even bother. If you plan to use setMany
, it can’t decode a Buffer embedded in an object.
The result looks OK in the template editor, but when I tried this it reaches the HA backend with the string wrapped in extra quotes: 'args': [[{'userCode': '"123456"', 'userId': 1, 'userIdStatus': 1}]]
.
I don’t think Z-Wave JS will accept that, but you’ll have to try (I don’t have a lock).
Searching around the forum, it sounds like HA will convert the template type to what it appears as, e.g. if it looks like a number, the type is a number. Doesn’t matter what you try it will persist (see [1 ] [2 ] [3 ]).
If the tojson
doesn’t work for you, and unless another solution is proposed, as a workaround you could prefix all of your user codes with 0
(assuming your lock keypad has a 0). The leading zero forces HA to treat it as a string. Not great, but seems to work:'args': [[{'userCode': '0123456', 'userId': 1, 'userIdStatus': 1}]]
There’s another trick of embedding the null byte at the end of the string. This works if you’re displaying something, but I doubt’ it will work when passing the data to Z-Wave JS.
userCode: >
{{ "{}\x00".format(states("sensor.otp_code")) }}
Results in: 'args': [[{'userCode': '123456\x00', 'userId': 1, 'userIdStatus': 1}]]
1 Like
freshcoast
(Z-Wave AI Bot)
April 15, 2024, 4:06pm
12
How about this:
service: zwave_js.invoke_cc_api
data:
command_class: "99"
method_name: setMany
parameters: |
{%
set codes = [
{ "userId": 2, "userIdStatus": 1, "userCode": states("sensor.otp_code") }
]
%}
{{ [codes] }}
It looks like the template resolver doesn’t recurse into an object when evaluating the values, avoiding the conversion to a number. Thanks @petro .
3 Likes
petro
(Petro)
April 15, 2024, 4:12pm
13
Yes, this should work if userCode
needs to be a string. I’ve used this work around to get past the resolver in the past.
change to
- service: xiaomi_miot.call_action
data_template:
entity_id: vacuum.alice_vacuum_mop
siid: 18
aiid: 1
params:
- piid: 1
value: 18
- "{{ {'piid':21, 'value': states('sensor.select_rooms_for_clean') } }}"
3 Likes
Thank you @petro and @freshcoast for helping me accomplish the ideal solution! Here’s my final automation, which includes an input_boolean
for disabling user codes:
alias: OTP Update Lock Codes
trigger:
- platform: state
entity_id:
- sensor.otp_user1
action:
- service: zwave_js.invoke_cc_api
target:
entity_id: lock.front_door
data:
command_class: 99
method_name: setMany
parameters: >-
{%
set codes = [
{ "userId": 2, "userIdStatus": 1 if
states("input_boolean.otp_user1_active") == "on" else 2, "userCode":
states("sensor.otp_user1") },
{ "userId": 3, "userIdStatus": 1 if
states("input_boolean.otp_user2_active") == "on" else 2, "userCode":
states("sensor.otp_user2") }
]
%}
{{ [codes] }}
1 Like