elchininet / custom-sidebar

Custom HACS plugin that allows you to personalise the Home Assistant's sidebar per user or device basis
Apache License 2.0
90 stars 6 forks source link
custom-plugin custom-plugins custom-sidebar custom-sidebars hacs hacs-plugin home-assistant homeassistant javascript lovelace lovelace-ui plugin

Custom Sidebar

hacs_badge HACS Action Tests Coverage Status downloads

Home Assistant Nightly Beta Tests

Custom HACS plugin that allows you to personalise the Home Assistant's sidebar per user or device basis

Default sidebar Hide/add some items Reorder items Personalise
                        
default sidebar
                        
                        
hide and new items
                        
                        
reorder items
                        
                        
personalise
                        

This is a refactor of custom-sidebar-v2 by @galloween, which unfortunately 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.

Installation

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.

Through HACS (v2 or greater)

[!NOTE] If your version of HACS is lower than v2 consult the section Through old HACS versions (< v2)

  1. Go to HACS dashboard
  2. Search for custom-sidebar and click on it
  3. On the plugin page, click on the Download yellow button in the bottom-right corner
  4. Click on Download in the more-info dialog
  5. When the plugin is already downloaded, add the url of the plugin as an extra_module_url in your configuration.yaml:

If you want to use a YAML configuration

frontend:
  extra_module_url:
    - /hacsfiles/custom-sidebar/custom-sidebar-yaml.js?v1.0.0

If you want to use a JSON configuration

frontend:
  extra_module_url:
    - /hacsfiles/custom-sidebar/custom-sidebar-json.js?v1.0.0
  1. Restart Home Assistant

[!TIP] It is recomendable that you add the plugin's version at the end of the URL as a parameter (e.g. ?v=1.0.0) to force Home Assistant to load the new version instead of a version stored in cache. This step is not mandatory, but if you start suffering from caching issues, this would be one of the first steps to perform.

Through old HACS versions (< v2)

  1. Go to HACS dashboard
  2. Go to Frontend
  3. Click on Explore and download repositories button in the bottom-right of the screen
  4. Search for custom-sidebar and install it
  5. Add the url of the plugin as an extra_module_url in your configuration.yaml:

If you want to use a YAML configuration

frontend:
  extra_module_url:
    - /hacsfiles/custom-sidebar/custom-sidebar-yaml.js?v1.0.0

If you want to use a JSON configuration

frontend:
  extra_module_url:
    - /hacsfiles/custom-sidebar/custom-sidebar-json.js?v1.0.0
  1. Restart Home Assistant

[!TIP] It is recomendable that you add the plugin's version at the end of the URL as a parameter (e.g. ?v=1.0.0) to force Home Assistant to load the new version instead of a version stored in cache. This step is not mandatory, but if you start suffering from caching issues, this would be one of the first steps to perform.

Manual installation

  1. Download the latest custom-sidebar release
  2. Copy custom-sidebar-yaml.js or custom-sidebar-json.js into <config directory>/www/ (depending on the configuration format that you are going to use, YAML or JSON)
  3. Add the url of the plugin as an extra_module_url in your configuration.yaml:

If you want to use a YAML configuration

frontend:
  extra_module_url:
    - /local/custom-sidebar-yaml.js?v1.0.0

If you want to use a JSON configuration

frontend:
  extra_module_url:
    - /local/custom-sidebar-json.js?v1.0.0
  1. Restart Home Assistant

[!TIP] It is recomendable that you add the plugin's version at the end of the URL as a parameter (e.g. ?v=1.0.0) to force Home Assistant to load the new version instead of a version stored in cache. This step is not mandatory, but if you start suffering from caching issues, this would be one of the first steps to perform.

Configuration

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.

[!TIP] In principle, using a YAML or a JSON configuration is a matter of taste and preference. If you don't plan to use templates, there is no advantage of one over the other, but if you plan to use templates, specially complex ones, YAML is recommended because you can write code blocks in multiple lines. As JSON doesn't allow line breaks, you would need to write the complete template in a single line.

Add a file named sidebar-config.yaml or sidebar-config.json into your <config directory>/www/ directory. It is recomendable that you copy the example sidebar-config.yaml or the example sidebar-config.json file, delete the id parameter, and modify it to match your needs. It is recommended to start with the simplest configuration and start to extend it step by step, in that way if you suffer any issue, it is easy to identify with which code block it started. If you want to check an extensive sidebar configuration which uses custom styles, variables and templates, check @Mariusthvdb's sidebar-config.yaml.

Configuration options

Property Type Required Description
order Array of items no List of items to process
title* String no Custom title to replace the Home Assistant title
title_color* String no Sets the color of the sidebar title
subtitle* String no Sets a custom subtitle below the Home Assistant title
subtitle_color* String no Sets the color of the sidebar subtitle
sidebar_background* String no Sets the background of the sidebar. It could be a color or a background declaration
sidebar_border_color* String no Sets the border-right color of the sidebar
menu_background* String no Sets the background of the sidebar menu area (the one containing the menu button and the title). If it is not set, the sidebar_background option will be used. It could be a color or a background declaration
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 a boolean or a promise that resolves in a boolean
sidebar_mode String no Defines the default status of the sidebar when Home Assistant is loaded. It has three possible values: "hidden" to make the sidebar hidden, "narrow" to make the sidebar visible in narrow state and "extended" to make sidebar visible in extended state. This option will show or hide the sidebar ignoring if it is a desktop or a mobile device or if the Always hide the sidebar switch in the profile page in on or off (depending on the value of this option, this switch will be switched on or off automatically)
sidebar_button_color* String no Sets the color of the sidebar hamburger menu
item_background* String no Sets the background of the sidebar items. It could be a color or a background declaration
item_background_hover* String no Sets the background of the sidebar items in a hover state. It could be a color or a background declaration
icon_color* String no Sets the color of the sidebar icons
icon_color_selected* String no Sets the icon color of the selected sidebar item
icon_color_hover* String no Sets the icon color of the sidebar items in a hover state
text_color* String no Sets the text color of the sidebar items
text_color_selected* String no Sets the text color of the selected sidebar item
text_color_hover* String no Sets the text color of the sidebar items in a hover state
selection_background* String no Sets the background of the selected item. It could be a color or a background declaration. If it is not specified, the icon_color_selected will be used
selection_opacity* Number or String no Sets the opacity of the selected item background. It should be a number between 0 (fully transparent) and 1 (fully opaque). If it is not specified, the default 0.12 will be used
info_color* String no Sets the color of the info texts of the sidebar items
info_color_selected* String no Sets the color of the info text of the selected sidebar item
info_color_hover* String no Sets the color of the info texts in a hover state
notification_color* String no Sets the color of the sidebar notifications
notification_color_selected* String no Sets the color of the sidebar notification of the selected sidebar item
notification_color_hover* String no Sets the color of the sidebar notification when the item is in hover state
notification_text_color* String no Sets the color of the sidebar notifications texts
notification_text_color_selected* String no Sets the color of the sidebar notification texts of the selected sidebar item
notification_text_color_hover* String no Sets the color of the sidebar notifications texts when the item is in hover state
divider_color* String no Sets the color of both sidebar dividers
divider_top_color* String no Sets the color of the top sidebar divider. It overrides divider_color for this divider if it is set
divider_bottom_color* String no Sets the color of the bottom sidebar divider. It overrides divider_color for this divider if it is set
scrollbar_thumb_color* String no Sets the color of the sidebar scrollbar (This option uses non-baseline CSS styles and it could not work in some browsers)
styles String no Custom styles that will be added to the styles block of the plugin. Useful to override styles
js_variables Object no An object containing variales that will be used in JavaScript templates. The variables cannot be templates and they must be strings, numbers or booleans
jinja_variables Object no An object containing variales that will be used in Jinja templates. The variables cannot be templates and they must be strings, numbers or booleans
partials Object no An object containing fragments of code that can be included in your templates. Consult the partial section for more info

[!TIP] * These options allow JavaScript or Jinja templates.

Order items properties

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).
order Number no Sets the order number of the sidebar item
hide Boolean no Setting this property to true will hide the sidebar item
name* String no Changes the name of the sidebar item
icon* String no Specifies the icon of the sidebar item
info* String no Sets the content of the info text (a smaller secondary text below the main item text)
notification* String no Add a notification badge to the sidebar item
item_background* String no Sets the background of the items. It could be a color or a background declaration (it overrides the global item_background)
item_background_hover* String no Sets the background of the items in a hover state. It could be a color or a background declaration (it overrides the global item_background_hover)
icon_color* String no Sets the color of the icon (it overrides the global icon_color)
icon_color_selected* String no Sets the icon color of the item when it is selected (it overrides the global icon_color_selected)
icon_color_hover* String no Sets the item icon color in a hover state (it overrides the global icon_color_hover)
text_color* String no Sets the text color of the item (it overrides the global text_color)
text_color_selected* String no Sets the text color of the item when it is selected (it overrides the global text_color_selected)
text_color_hover* String no Sets the item text color in a hover state (it overrides the global text_color_hover)
selection_background* String no Sets the background of the item when it is selected. It could be a color or a background declaration. If it is not specified, the icon_color_selected will be used
selection_opacity* Number or String no Sets the opacity of the item background when it is selected. It should be a number between 0 (fully transparent) and 1 (fully opaque). If it is not specified, the default 0.12 will be used (it overrides the global selection_opacity)
info_color* String no Sets the color of the info text (it overrides the global info_color)
info_color_selected* String no Sets the color of the info text when the item is selected (it overrides the global info_color_selected)
info_color_hover* String no Sets the color of the info text in a hover state (it overrides the global info_color_hover)
notification_color* String no Sets the notification color (it overrides the global notification_color)
notification_color_selected* String no Sets the color of the item notification when it is selected (it overrides the global notification_color_selected)
notification_color_hover* String no Sets the color of the item notification in hover state (it overrides the global notification_color_hover)
notification_text_color* String no Sets the color of the notification text (it overrides the global notification_text_color)
notification_text_color_selected* String no Sets the color of the notification text when it is selected (it overrides the global notification_text_color_selected)
notification_text_color_hover* String no Sets the color of the notifications text in hover state (it overrides the global notification_text_color_hover)
bottom Boolean no Setting this property to true will group the item with the bottom items (Configuration, Developer Tools, etc)
href String no Specifies the href of the sidebar item
target String no Specifies the target property 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

[!TIP] * These item properties allow JavaScript or Jinja templates.

Short example in YAML format:

title: My Home
icon_color_selected: var(--accent-color)
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: Automations
    href: "/config/automation"
    icon: mdi:robot
    info: |
      {{ states.automation | selectattr('state', 'eq', 'on') | list | count }} active
    order: 3

Short example in JSON format:

{
  "title": "My Home",
  "icon_color_selected": "var(--accent-color)",
  "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": "Automations",
      "href": "/config/automation",
      "icon": "mdi:robot",
      "info": "{{ states.automation | selectattr('state', 'eq', 'on') | list | count }} active",
      "order": 3
    }
  ]
 }

[!IMPORTANT]

  • All items in config.order should have a unique item property
  • Avoid an item property that could match multiple elements. If you find that an item property matches with multiple elements, try to use the match and exact 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 in at least one item, 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 their bottom property on true.
  • 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 main sidebar items so they cannot be targeted inside the order option to change their properties. On the other hand, global color properties will affect these items though.
  • The style option doesn't allow templates, it should be used only to override or correct some styles of the sidebar.

Exceptions

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. In an exception you can define almost all the options available in the main configuration options (excluding js_variables, jinja_variables and partials). And on top of those options, the next ones will be available:

Property Type Required Description
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)

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": [
          ...
      ]
    }
  ]  
}

[!IMPORTANT]

  • You cannot use user and not_user at the same time, doing so will end in an error
  • You cannot use device and not_device at the same time, doing so will end in an error
  • Pay attention to extend_from_base property. If it's set to false (default value), the main config will be ignored, leaving you with default sidebar modified only by the exception's options

Templates

Some config options and item properties, as title, sidebar_editable, name notification, and info, 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 and can return Promises, and Jinja templates are mostly agnostic to the device in which Home Assistant is being executed.

JavaScript templates

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 used in the templates will be stored, so if the state of an stored entity changes, all the templates that use this entity will be reevaluated snd rerendered. On top of this, if the variable panel_url is used in a template, the template will be reevaluated every time that a new panel or a new view is loaded.

JavaScript templates example

The next example will set the next options:

  1. Sets the title of the sidebar as "My Home" followed by the current time.
  2. Sets the background of the sidebar red when the panel config is selected and green otherwise.
  3. Adds 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.
  4. 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 and the Operating System version in the info text.
in YAML format:
title: '[[[ "My Home " + new Date(states("sensor.date_time_iso")).toLocaleTimeString().slice(0, 5) ]]]'
sidebar_background: |
  [[[
    return panel_url === '/config/dashboard'
      ? 'red'
      : 'green';
  ]]]
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") + ")" ]]]'
    info: '[[[ return "OS " + state_attr("update.home_assistant_operating_system_update", "latest_version") ]]]'
    href: '/config/info'
    icon: mdi:information-outline
in JSON format:
{
  "title": "[[[ 'My Home ' + new Date(states('sensor.date_time_iso')).toLocaleTimeString().slice(0, 5) ]]]",
  "sidebar_background": "[[[ return panel_url === '/config/dashboard' ? 'red' : 'green' ]]]",
  "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') + ')' ]]]",
      "info": "[[[ return 'OS ' + state_attr('update.home_assistant_operating_system_update', 'latest_version') ]]]",
      "href": "/config/info",
      "icon": "mdi:information-outline"
    }
  ]
}

[!TIP] Custom Sidebar uses Home Assistant Javascript Templates for the JavaScript templating system. To know all the objects, variables and methods available in the JavaScript templates, consult the proper section in the repository.

Jinja templates

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 desired option. 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:

When the entities used in a templates change their state, it will trigger an update and the updated result of the template will be rendered.

Jinja templates example

The next example will set the next options:

  1. Sets the title of the sidebar as "My Home" followed by the current time.
  2. Adds 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.
  3. 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 and the Operating System version in the info text.
in YAML format:
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") }})'
    info: 'OS {{ state_attr("update.home_assistant_operating_system_update", "latest_version") }}'
    href: '/config/info'
    icon: mdi:information-outline
in JSON format:
{
  "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') }})",
      "info": "OS {{ state_attr('update.home_assistant_operating_system_update', 'latest_version') }}",
      "href": "/config/info",
      "icon": "mdi:information-outline"
    }
  ]
}

Partials

Partials are fragments of code that can be included in your templates. They can be inserted in JavaScript, Jinja templates or inside another partial. Any entity used in them will make the template in which the partial is inserted to be reevaluated when the entity changes its state, so it is not recommended to use a bloated partial using multiple entities that have no context with each other because that will provoke that the templates in which that partial is used get reevaluated when anyone of the entities used in it change.

[!TIP] Partials will automatically use the variables set in the js_variables or jinja_variables (depending on the kind of template in which they are inserted).

Partials example with a JavaScript template

in YAML format:
js_variables:
  supervisor_update: update.home_assistant_supervisor_update
  os_update: update.home_assistant_operating_system_update
partials:
  supervisor_version: |
    const supervisorVersion = state_attr(supervisor_update, "latest_version");
  updates: |
    @partial supervisor_version
    const osVersion = state_attr(os_update, "latest_version");
order:
  - new_item: true
    item: info
    name: |
      [[[
        @partial updates
        return `Info ${supervisorVersion}`;
      ]]]
    info: |
      [[[
        @partial updates
        return `OS ${ osVersion }`
      ]]]
    href: '/config/info'
    icon: mdi:information-outline
in JSON format:
{
  "js_variables": {
    "supervisor_update": "update.home_assistant_supervisor_update",
    "os_update": "update.home_assistant_operating_system_update"
  },
  "partials": {
    "supervisor_version": "const supervisorVersion = state_attr(supervisor_update, 'latest_version');",
    "updates": "@partial supervisor_version const osVersion = state_attr(os_update, 'latest_version');"
  },
  "order": [
    {
      "new_item": true,
      "item": "info",
      "name": "[[[ @partial updates return `Info ${supervisorVersion}`; ]]]",
      "info": "[[[ @partial updates return `OS ${ osVersion }`; ]]]",
      "href": "/config/info",
      "icon": "mdi:information-outline"
    }
  ]
}

Partials example with a Jinja template

in YAML format:
jinja_variables:
  supervisor_update: update.home_assistant_supervisor_update
  os_update: update.home_assistant_operating_system_update
partials:
  supervisor_version: |
    {% set supervisorVersion = state_attr(supervisor_update, "latest_version") %}
  updates: |
    @partial supervisor_version
    {% set osVersion = state_attr(os_update, "latest_version") %}
order:
  - new_item: true
    item: info
    name: |
        @partial updates
        Info {{ supervisorVersion }}
    info: |
        @partial updates
        OS {{ osVersion }}
    href: '/config/info'
    icon: mdi:information-outline
in JSON format:
{
  "jinja_variables": {
    "supervisor_update": "update.home_assistant_supervisor_update",
    "os_update": "update.home_assistant_operating_system_update"
  },
  "partials": {
    "supervisor_version": "{% set supervisorVersion = state_attr(supervisor_update, 'latest_version') %}",
    "updates": "@partial supervisor_version {% set osVersion = state_attr(os_update, 'latest_version') %}"
  },
  "order": [
    {
      "new_item": true,
      "item": "info",
      "name": "@partial updates Info {{ supervisorVersion }}",
      "info": "@partial updates OS {{ osVersion }}",
      "href": "/config/info",
      "icon": "mdi:information-outline"
    }
  ]
}

Home Assistant built-in sidebar configuration options

Check out Home Assistant's native sidebar tools, maybe it will be enough for your needs.

Custom-sidebar extras


Credits and huge thanks to: