Custom HACS plugin that allows you to manage Home Assistant's sidebar items per user or device basis.
Default sidebar | Hide some items | Add new items | Reorder items |
---|---|---|---|
This is a refactor of custom-sidebar-v2 by @galloween, which unfortunatelly is now unmaintained and archived. In its beginning, @galloween's code was a refactor of the original Custom Sidebar plugin by @Villhellm (R.I.P.). This version refactored completely @galloween's code allowing to use a configuration in YAML
(as @Villhellm's one) or in JSON
(as @galloween's version), fixing several bugs, improving performance, and using home-assistant-query-selector to make it less likely to break with future Home Assistant front-end updates.
You can install the plugin manually or through HACS, not both. If you install the plugin using the two installations methods you could have issues or errors.
Note: if your version of
HACS
is lower thanv2
consult the section Through old HACS versions (< v2)
- Go to
HACS
dashboard - Search for
custom-sidebar
and click on it - On the plugin page, click on the
Download
yellow button in the bottom-right corner - Click on
Download
in the more-info dialog - When the plugin is already downloaded, add the url of the plugin as an extra_module_url in your
configuration.yaml
:
frontend:
extra_module_url:
- /hacsfiles/custom-sidebar/custom-sidebar-yaml.js?v1.0.0
frontend:
extra_module_url:
- /hacsfiles/custom-sidebar/custom-sidebar-json.js?v1.0.0
- Make sure you add the correct version at the end of the URL (e.g.
?v=1.0.0
) because in this way you make Home Assistant to load the new version instead of a version stored in cache - Restart Home Assistant
- Go to
HACS
dashboard - Go to
Frontend
- Click on
Explore and download repositories
button in the bottom-right of the screen - Search for
custom-sidebar
and install it - Add the url of the plugin as an extra_module_url in your
configuration.yaml
:
frontend:
extra_module_url:
- /hacsfiles/custom-sidebar/custom-sidebar-yaml.js?v1.0.0
frontend:
extra_module_url:
- /hacsfiles/custom-sidebar/custom-sidebar-json.js?v1.0.0
- Make sure you add the correct version at the end of the URL (e.g.
?v=1.0.0
) because in this way you make Home Assistant to load the new version instead of a version stored in cache - Restart Home Assistant
- Download the latest custom-sidebar release
- Copy
custom-sidebar-yaml.js
orcustom-sidebar-json.js
into<config directory>/www/
(depending on the configuration format that you are going to use,YAML
orJSON
) - Add the url of the plugin as an extra_module_url in your
configuration.yaml
:
frontend:
extra_module_url:
- /local/custom-sidebar-yaml.js?v1.0.0
frontend:
extra_module_url:
- /local/custom-sidebar-json.js?v1.0.0
- Make sure you add the correct version at the end of the URL (e.g.
?v=1.0.0
) because in this way you make Home Assistant to load the new version instead of a version stored in cache - Restart Home Assistant
Depending on the file that you have added to extra_module_url, you will need to add your configuration in YAML
or JSON
format. If you used custom-sidebar-yaml.js
you need to provide the configuration in YAML
format. If you have used custom-sidebar-json.js
you need to provide the configuration in JSON
format.
Add a file named sidebar-config.yaml
or sidebar-config.json
into your <config directory>/www/
directory. It could be easier if you copy the example sidebar-config.yaml or the example sidebar-config.json file, delete the id
parameter, and edit it to match your needs.
Property | Type | Required | Description |
---|---|---|---|
order | Array of item | yes | List of items to process |
title* | String | no | Custom title to replace the Home Assistant title |
sidebar_editable* | Boolean or String | no | If it is set to false, long press on the sidebar title will be ignored and the button to edit the sidebar in the profile panel will be disabled. As a string it should be a JavaScript or a Jinja template that returns true or false |
styles | String | no | Custom styles that will be added to the styles block of the plugin |
Property | Type | Required | Description |
---|---|---|---|
item | String | yes | This is a string that will be used to match each sidebar item by its text, its data-panel attribute or its href . If the exact property is not set, it is case insensitive and it can be a substring such as developer instead of Developer Tools or KITCHEN instead of kitchen-lights . |
match | String | no | This property will define which string will be used to match the item property. It has three possible values "text" (default) to match the text content of the element, "data-panel" to match the data-panel attribute of the element, or "href", to match the href attribute of the element |
exact | Boolean | no | Specifies whether the item string match should be an exact match (true ) or not (false ). |
name* | String | no | Changes the name of the sidebar item |
notification* | String | no | Add a notification badge to the sidebar item |
order | Number | no | Sets the order number of the sidebar item |
bottom | Boolean | no | Setting this property to true will group the item with the bottom items (Configuration, Developer Tools, etc) |
hide | Boolean | no | Setting this property to true will hide the sidebar item |
href | String | no | Specifies the href of the sidebar item |
target | String | no | Specifies the target property of the sidebar item |
icon | String | no | Specifies the icon of the sidebar item |
new_item | Boolean | no | Set this property to true to create a new item in the sidebar. Using this option makes href and icon required properties |
* These options and item properties allow JavaScript or Jinja templates.
Short example in YAML
format:
title: My Home
order:
- new_item: true
item: Google
href: https://mrdoob.com/projects/chromeexperiments/google-gravity/
icon: mdi:earth
target: _blank
order: 1
- item: overview
order: 2
- new_item: true
item: Integrations
href: "/config/integrations"
icon: mdi:puzzle
order: 3
styles: |
paper-listbox {
--icon-primary-color: var(--accent-color);
}
Short example in JSON
format:
{
"title": "My Home",
"order": [
{
"new_item": true,
"item": "Google",
"href": "https://mrdoob.com/projects/chromeexperiments/google-gravity/",
"icon": "mdi:earth",
"target": "_blank",
"order": 1
},
{
"item": "overview",
"order": 2
},
{
"new_item": true,
"item": "Integrations",
"href": "/config/integrations",
"icon": "mdi:puzzle",
"order": 3
}
],
"styles": "paper-listbox { --icon-primary-color: var(--accent-color); }"
}
- All items in
config.order
should have a uniqueitem
property - Avoid an
item
property that could match multiple elements. If you find that an item property matches with multiple elements, try to use thematch
andexact
properties to match the specific item that you want to match. - The items will be ordered according to their
order
property OR in the order of appearance - If you use the
order
property, make sure either all items (except hidden ones) have this property, or none of them (otherwise order may be messed up) - All the items placed in the bottom will be moved to the top by default. If you want to have some items in the bottom you need to add them to the
config.order
and specify theirbottom
property ontrue
. - Any items present in the Sidebar, but not in
config.order
, will be shown on the bottom of the top part of the list - Notifications and user account are not part of the sidebar items so they will not be processed by this plugin
You can define user-specific options using exceptions feature. Exceptions can be used if you would like to define different options for a specific user/device.
Property | Type | Required | Description |
---|---|---|---|
order | Array of item | no | Defines the sidebar items order |
title* | String | no | Custom title to replace the Home Assistant title |
sidebar_editable* | Boolean or String | no | If it is set to false, long press on the sidebar title will be ignored and the button to edit the sidebar in the profile panel will be disabled. As a string it should be a JavaScript or a Jinja template that returns true or false |
styles | String | no | Custom styles that will be added to the styles block of the plugin |
extend_from_base | Boolean | no | If true, the options will be extended with the root options. The property order will be merged with the base one, the rest of properties will use the base counterpart if they are not specified. If it is false, it will take into account only the options in the exception |
user | String or String[] | no | Home Assistant user name(s) you would like to display this order for |
not_user | String or String[] | no | Home Assistant user name(s) you wouldn't like to display this order for |
device | String or String[] | no | Device(s) you would like to display this order for. E.g. ipad, iphone, macintosh, windows, android (it uses the device's user-agent) |
not_device | String or String[] | no | Device(s) you wouldn't like to display this order for. E.g. ipad, iphone, macintosh, windows, android (it uses the device's user-agent) |
* These options and item properties allow JavaScript or Jinja templates.
Short example in YAML
format:
...
exceptions:
- user:
- Jim Hawkins
- Long John Silver
title: My Home
order:
...
Short example in JSON
format:
{
...
"exceptions": [
{
"user": ["Jim Hawkins", "Long John Silver"],
"title": "My Home",
"order": [
...
]
}
]
}
- You cannot use
user
andnot_user
at the same time, doing so will end in an error - You cannot use
device
andnot_device
at the same time, doing so will end in an error - Pay attention to
extend_from_base
property. If it's set tofalse
(default value), the mainconfig
will be ignored, leaving you with default sidebar modified only by the exception's options
Some config options and item properties, as title
, sidebar_editable
, name
and notification
, admit templates. custom-sidebar
admits two templating systems, JavaScript templates or Jinja templates. JavaScript
templates are processed faster because the rendering is done in client side, Jinja
templates need to perform a websocket call to receive the template result, but in general you should not notice many differences between the two in terms of performance. The main difference between the two templating systems (apart from the syntax) is that JavaScript
can access client side data like DOM APIs meanwhile Jinja
templates are agnostic to the device in which Home Assistant
is being executed.
This templating system IS NOT the same that Home Assistant implements. It is basically a JavaScript
code block in which you can use certain client-side objects, variables and methods. To set a property as a JavaScript
template block, include the code between three square brackets [[[ JavaScript code ]]]
. If you don‘t use the square brackets, the property will be interpreted as a regular string.
The JavaScript
code will be taken as something that you want to return, but if you have a more complex logic, you can create your own variables and return the desired result at the end.
The entities and domains used in the templates will be stored so if the state of these entities change, it will update the templates used in the configuration.
The next example will set the title of the sidebar as "My Home" followed by the current time. It will also add the number of HACS
updates as a notification in the HACS
item in the sidebar. In case that there are no updates, an empty string is returned and in these cases the notification will not be displayed. And it also creates a new item that redirects to the Home Assistant
info page with a dynamic text with the word "Info" followed by the installed Supervisor version between parentheses.
title: '[[[ "My Home " + new Date(states("sensor.date_time_iso")).toLocaleTimeString().slice(0, 5) ]]]'
order:
- item: hacs
notification: |
[[[
const outdatedHacsEntities = Object.values(entities.update).filter(
(entity) => entity.platform === 'hacs' && is_state(entity.entity_id, 'on')
);
return outdatedHacsEntities.length || '';
]]]
- new_item: true
item: info
name: '[[[ "Info (" + state_attr("update.home_assistant_supervisor_update", "latest_version") + ")" ]]]'
href: '/config/info'
icon: mdi:information-outline
{
"title": "[[[ 'My Home ' + new Date(states('sensor.date_time_iso')).toLocaleTimeString().slice(0, 5) ]]]",
"order": [
{
"item": "hacs",
"notification": "[[[ return Object.values(entities.update).filter((entity) => entity.platform === 'hacs' && is_state(entity.entity_id, 'on')).length || '' ]]]"
},
{
"new_item": true,
"item": "info",
"name": "[[[ 'Info (' + state_attr('update.home_assistant_supervisor_update', 'latest_version') + ')' ]]]",
"href": "/config/info",
"icon": "mdi:information-outline"
}
]
}
Note:
Custom Sidebar
uses Home Assistant Javascript Templates for theJavaScript
templating system. To know all the objects, variables and methods available in theJavaScript
templates, consult the proper section in the repository.
This templating system is the same that Home Assistant implements. You can use the majority of the template methods and objects. The entire template will be processed, rendered and the result will be used as the inner html of the element. If you don‘t want to display anything in certain scenarios, you should return an empty string in those cases. The next client side varianles will be available in Jinja templates
:
user_name
: String with the logged user's nameuser_is_admin
: Bolean value than indicates if the logged user is admin or notuser_is_owner
: Bolean value than indicates if the logged user is the owner or notuser_agent
: User agent of the browser in which Home Assistant is being executed
When the entities and domains used in a templates change, it will trigger an update and the updated result of the template will be rendered.
The next example will set the title of the sidebar as "My Home" followed by the current time. It will also add the number of HACS
updates as a notification in the HACS
item in the sidebar. In case that there are no updates, an empty string is returned and in these cases the notification will not be displayed. And it also creates a new item that redirects to the Home Assistant
info page with a dynamic text with the word "Info" followed by the installed Supervisor version between parentheses.
title: 'My Home {{ as_timestamp(states("sensor.date_time_iso")) | timestamp_custom("%H:%M") }}'
order:
- item: hacs
notification: |
{{
expand(states.update)
| selectattr('state', 'eq', 'on')
| map(attribute='entity_id')
| map('device_attr', 'identifiers')
| map('contains', 'hacs')
| list
| length or ""
}}
- new_item: true
item: info
name: 'Info ({{ state_attr("update.home_assistant_supervisor_update", "latest_version") }})'
href: '/config/info'
icon: mdi:information-outline
{
"title": "My Home {{ as_timestamp(states('sensor.date_time_iso')) | timestamp_custom('%H:%M') }}",
"order": [
{
"item": "hacs",
"notification": "{{ expand(states.update) | selectattr('state', 'eq', 'on') | map(attribute='entity_id') | map('device_attr', 'identifiers') | map('contains', 'hacs') | list | length or '' }}"
},
{
"new_item": true,
"item": "info",
"name": "Info ({{ state_attr('update.home_assistant_supervisor_update', 'latest_version') }})",
"href": "/config/info",
"icon": "mdi:information-outline"
}
]
}
Check out Home Assistant's native sidebar tools, maybe it will be enough for your needs.
- You can use Home Assistant's
panel_custom
integration to add internal links to the sidebar. Take a look at this tutorial. Official docs. - You can use Home Assistant's
Webpage dashboard
feature to add external URLs to Home Assistant's sidebar. Official docs. If you use Home Assistant'sWebpage dashboard
feature to add some wepage dashboards to your sidebar, then you can modify them as the regular ones adding them to theorder
property. - You can click and hold the Home Assistant header on top of the sidebar and then it will allow you to add/remove and re-order some of the items (but not add new custom ones). This feature is also accessible from your profile settings (if you click on your username in the bottom left corner). The drawback of this feature is that it only works per device/session, so you need to log-in in the specific device and do these changes, but if you log-out, the changes will be lost.