Voluptuous Schema and matches_regex

I’m the creator of the Visonic Alarm Integration in HACS and I’m trying to update the Configuration settings to make it more clear and robust.

I currently have this for setting the override code in the Configuration.

import voluptuous as vol
from homeassistant.helpers import config_validation as cv

CONF_OVERRIDE_CODE = "override_code"

.........

vol.Optional(
    CONF_OVERRIDE_CODE, default=self.create_default(options, CONF_OVERRIDE_CODE, "")
): cv.string,

The self.create_default function looks up the setting from last time and fills it in as the default for the text box, if it’s not there then it sets it to the empty string.

It almost works in that it displays a text box in the form to enter a text string. There are 2 problems with this:

  • There is no validation that the user has to enter a 4 digit numeric code
  • Once the user enters a code, there is no way to delete it and empty the text box. I think this is a bug in Home Assistant somewhere. On a Configuration Form the user should be able to delete the content of the default and empty the text box, the form should return an empty string but it returns the default value.

In any case, I tried to update it using a regex validation like this to get it to be more robust:

vol.Optional(
    CONF_OVERRIDE_CODE, default=self.create_default(options, CONF_OVERRIDE_CODE, "")
): cv.matches_regex("(^[0-9][0-9][0-9][0-9]$|^$)"),

I’m assuming that cv.matches_regex will create a text box in the form in the same way as cv.string but I haven’t yet got it to work so I’m not sure.

This regex should allow the user to enter the empty string or a 4 digit code but it crashes with the following error:

2022-08-13 11:47:47.424 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/aiohttp/web_protocol.py", line 435, in _handle_request
    resp = await request_handler(request)
  File "/usr/local/lib/python3.10/site-packages/aiohttp/web_app.py", line 504, in _handle
    resp = await handler(request)
  File "/usr/local/lib/python3.10/site-packages/aiohttp/web_middlewares.py", line 117, in impl
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/security_filter.py", line 60, in security_filter_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/forwarded.py", line 94, in forwarded_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/request_context.py", line 28, in request_context_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/ban.py", line 82, in ban_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/auth.py", line 236, in auth_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/view.py", line 136, in handle
    result = await result
  File "/usr/src/homeassistant/homeassistant/components/config/config_entries.py", line 215, in post
    return await super().post(request)
  File "/usr/src/homeassistant/homeassistant/components/http/data_validator.py", line 73, in wrapper
    result = await method(view, request, data, *args, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/data_entry_flow.py", line 84, in post
    result = self._prepare_result_json(result)
  File "/usr/src/homeassistant/homeassistant/helpers/data_entry_flow.py", line 43, in _prepare_result_json
    data["data_schema"] = voluptuous_serialize.convert(
  File "/usr/local/lib/python3.10/site-packages/voluptuous_serialize/__init__.py", line 40, in convert
    pval = convert(value, custom_serializer=custom_serializer)
  File "/usr/local/lib/python3.10/site-packages/voluptuous_serialize/__init__.py", line 113, in convert
    if issubclass(schema, Enum):
TypeError: issubclass() arg 1 must be a class

I have changed the regex but it doesn’t make a difference.

Please help, what am I doing wrong?

I’ve been experimenting for a few hours and I’ve tried other functions in the helpers config_validation.

I changed my schema to this to set the default to the empty string.

vol.Optional(
    CONF_OVERRIDE_CODE, default=""
): cv.string,

cv.string works (as it did before) but the following do not, they give the same ERROR as above:
cv.url, cv.string_with_no_html, cv.whitespace

I also copied the code for cv.string in to my own code and referred to that, I get the same error.
In fact the only thing that works is cv.string.

In summary, Python built in types work e.g. int, str, bool and I have used cv.string and cv.multi_select that also works.

The only thing I can think of is that Home Assistant depends on the built in types and a small subset of the helpers in config_validation for creating configuration forms. If this is the case, where do I find the list of valid types?

Dave, hard to see exactly what this issue might be without rest of code but this link uses matches_regex in the config schema and may point you in the right direction.

Hi and thanks for replying and trying. It is used in that Integration however the CONFIG_ENTRY_SCHEMA variable isn’t used so it wouldn’t get called.
My source code is here, line 185
As you can see commented at the end of the line is my latest try at it.

Dave, sorry not had chance today to test any code but done a little code reviewing and think the following.

  1. it does look like you cant use cv.matches_regex in config flows - as you have seen
  2. there is a vol.Match but not 100% sure whether this will also fail from looking at the code but if you wrap it in a vol.All that may do.
  3. it looks like you can also use a custom validator as per.
    validation - How can i validate an url and email with python voluptuous? - Stack Overflow

I can have more of a play for you later if that helps. Also, maybe worth asking on discord if anyone can help you as the main devs of HA seem active on there.

I went back and looked at the error trace and spotted this

custom_serializer=custom_serializer

I also looked at the source code for voluptuous_serialize

This combination calls the custom_serializer function on line 954 in the HA source code here

It looks like the HA backend is serialising the type to send to the frontend for the GUI to display.
Looking at custom_serializer, the only types that it serialises are:

  • positive_time_period_dict
  • string
  • boolean
  • multi_select
  • selector

So that’s it I think. If it isn’t one of these types in config_validation.py then it can’t be used in a config flow. The library voluptuous_serialize serialises standard python types but this custom serialiser is called first.

So we can’t just use any of the validators in homeassistant/helpers/config_validation.py, they need to be serialised and only the list above are serialised.

I did post on discord and it was there that someone suggested using a selector which is the new HA blueprint way of doing things:

selector.NumberSelector(selector.NumberSelectorConfig(min=0, max=9999, mode=selector.NumberSelectorMode.BOX))

So that’s what I changed it to and uploaded a new release of the Integration to Github here.

The selectors are listed here

I think that’s pretty much it for now, perhaps the core team will add more to the selector in future.
Thanks again

@davesmeghead Thank you very much for pointing out that the blueprint selectors could be used in config_flow and an example of how to do it. This saved me a ton of time and headache.