# Bubble Modules # # How to use: # # - To edit or add modules (global styles and templates), first copy the # "bubble-modules.yaml" file from "/www/community/Bubble-Card/" (if installed via HACS) # to "/www/bubble/" (you'll need to create this folder). # # Then add these lines in your "configuration.yaml" under "homeassistant:": # # homeassistant: # allowlist_external_dirs: # - /config/www/bubble # # This step is not needed if you already have this line: # - /config/www # # Then save and restart Home Assistant. # # - After making changes, you need to refresh your page to apply the modifications. # # - Styles and templates defined under "default:" are applied globally to all cards by default. # # - See the final example in this file for further details on how to create a module. # # - You can share/find modules here: # github.com/Clooos/Bubble-Card/discussions/categories/share-your-modules default: name: Default description: Empty and enabled by default. Move your styles/templates here in the bubble-modules.yaml file to apply them to all cards. code: | /* CSS or JS templates (see examples below) */ home-assistant-default: name: Home Assistant default styling version: "v1.1" creator: "Clooos" link: "https://github.com/Clooos/Bubble-Card/discussions/1230" description: This module applies Home Assistant’s default styling to Bubble Card. To set it as the default, move it under default: in the bubble-modules.yaml file. code: | :host { --bubble-button-accent-color: rgba(0,140,255,0.3); /* Edit this color if needed */ --bubble-main-background-color: var(--ha-card-background, var(--card-background-color, #fff)); --bubble-border-radius: var(--ha-card-border-radius, 12px); --bubble-icon-border-radius: 32px; --bubble-button-border-radius: var(--bubble-border-radius); --bubble-climate-button-background-color: var(--bubble-icon-background-color); --bubble-border: var(--ha-card-border-width, 1px) solid var(--ha-card-border-color, var(--divider-color, #e0e0e0)); --bubble-secondary-background-color: transparent; } .bubble-container { -webkit-backdrop-filter: var(--ha-card-backdrop-filter, none); backdrop-filter: var(--ha-card-backdrop-filter, none); box-shadow: var(--ha-card-box-shadow, none); box-sizing: border-box; } .bubble-icon-container, .large .bubble-icon-container { --mdc-icon-size: 22px; min-width: 36px !important; min-height: 36px !important; } .large .bubble-cover-card-container > .bubble-buttons { --bubble-cover-main-background-color: none; } .bubble-range-fill { --bubble-accent-color: var(--bubble-button-accent-color); } .bubble-sub-button.background-on::before, .bubble-sub-button.background-off::before, .bubble-temperature-container::before, .bubble-icon-container::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: var(--control-number-buttons-background-opacity, .2); border-radius: var(--bubble-border-radius); background: var(--control-number-buttons-background-color, var(--disabled-color)); } .is-on { --bubble-icon-background-color: var(--view-background,var(--lovelace-background,var(--primary-background-color))); transition: all ease-in 0.3s !important; } .bubble-icon-container::before { background: var(--state-inactive-color); border-radius: var(--bubble-icon-border-radius); } .bubble-sub-button { border: 0px solid transparent !important; } .no-icon-select-arrow { right: 4px !important; } .large .bubble-icon-container { margin-left: 9px; } .bubble-state { opacity: 1; font-weight: 400; font-size: 12px; letter-spacing: .4px; } :not(.bubble-separator) > .bubble-name { font-weight: 500; font-size: 14px; letter-spacing: 0.1px; } .bubble-pop-up-background { filter: brightness(0.96); /* Improve pop-up background contrast */ --bubble-pop-up-border-radius: calc(var(--ha-card-border-radius, 12px) * 1.4); } .bubble-header-container { --bubble-secondary-background-color: var(--background-color-2); } ha-select { --bubble-list-item-accent-color: none !important; --mdc-theme-surface: var(--card-background-color); } mwc-list-item[selected] { color: inherit !important; --mdc-ripple-press-opacity: 0 !important; } mwc-list-item[selected]::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: var(--primary-color); opacity: 0.24; } icon_container_color: name: "Example: Customize the icon container color" version: "v1.1" creator: "Clooos" link: "https://github.com/Clooos/Bubble-Card/discussions/1231" unsupported: - horizontal-buttons-stack - separator description: | A list of predefined colors to customize the icon container color. Configure this module via the editor or in YAML, for example:

        icon_container_color: 
            color: light-blue
        
code: | .bubble-icon-container { opacity: 1 !important; background: var(--${this.config.icon_container_color?.color}-color) !important; } editor: - name: color label: "Color" selector: ui_color: include_none: true get_state_attribute: # Some informations about your module (this is shown in the editor) name: "Advanced example: Get state/attribute from other entities" version: "v1.1" creator: "Clooos" link: "https://github.com/Clooos/Bubble-Card" # Disable this module for unsupported "card_type" unsupported: - horizontal-buttons-stack - separator # The description have HTML support like in this example. This model is my favorite. description: | Get state/attribute from other entities and replace the default state/attribute field. Configure this module via the editor or in YAML, for example:

        get_state_attribute:
            - entity: weather.home
            - entity: sensor.weather_station
              attribute: humidity
            - entity: sensor.weather_station
              attribute: temperature
        

If it doesn't work, make sure at least one of "Show state" or "Show attribute" is turned on in your card configuration. # Code blocks must always start with ${(() => { and end with })()} # Inline codes must always start with ${ and end with } # # This section only supports JavaScript and/or CSS code: | ${(() => { // Retrieve the configuration or use an empty array by default const config = this.config.get_state_attribute || []; // Format the retrieved value from the entity for each entry const values = config .map(cfg => { const entity = hass.states[cfg.entity]; if (entity) { return cfg.attribute ? hass.formatEntityAttributeValue(entity, cfg.attribute) : hass.formatEntityState(entity); } return null; }) .filter(value => value !== null); // Remove null values // Update the DOM element with the class 'bubble-state' // displaying values separated by ' • ' card.querySelector('.bubble-state').innerText = values.join(' • '); })()} # The editor part can be tricky, but improves modules a lot! # # In this example, it will create your configuration to this variable: # this.config.get_state_attribute # # To understand what you can do here, take a look at the "ha-form" sources here: # https://github.com/home-assistant/frontend/tree/03a415beff6e6f9c87a95287804f6c03c8fef3d5/src/components/ha-form # # And here for the selectors: # https://github.com/home-assistant/frontend/blob/03a415beff6e6f9c87a95287804f6c03c8fef3d5/src/data/selector.ts # # I will try to document that for clarity! editor: - type: expandable title: "Select entities and attributes" icon: "mdi:list-box-outline" schema: - name: '0' type: expandable title: "Entity 1" schema: - name: entity label: "Entity" selector: entity: {} - name: attribute label: "Attribute" selector: attribute: {} - name: '1' type: expandable title: "Entity 2" schema: - name: entity label: "Entity" selector: entity: {} - name: attribute label: "Attribute" selector: attribute: {} - name: '2' type: expandable title: "Entity 3" schema: - name: entity label: "Entity" selector: entity: {} - name: attribute label: "Attribute" selector: attribute: {} - name: '3' type: expandable title: "Entity 4" schema: - name: entity label: "Entity" selector: entity: {} - name: attribute label: "Attribute" selector: attribute: {} # Temperature temperature_colouring: name: "Temperature colouring" version: "v1.0" creator: "Timmy" unsupported: - horizontal-buttons-stack - media-player description: | This module provides dynamic colouring based on temperature sensor values. Colour adjustments are applied to selected sub-buttons and/or state display elements. Required entities: - A temperature sensor You can configure which elements should receive dynamic colouring: - Sub-buttons (1-6) - State display Example YAML configuration: temperature_colouring: temperature_sensor: sensor.kitchen_temperature elements: sub_buttons: [1, 3, 5] # Apply to sub-buttons 1, 3, and 5 state_display: true # Apply to state display element code: | ${(() => { const temperature = parseFloat(hass?.states[this.config?.temperature_colouring?.temperature_sensor]?.state || 0); const elements = this.config?.temperature_colouring?.elements || {}; let color; if (temperature <= 32) { color = 'rgba(0,0,139,0.8)'; } else if (temperature <= 41) { color = 'rgba(0,71,171,0.8)'; } else if (temperature <= 50) { color = 'rgba(30,144,255,0.8)'; } else if (temperature <= 61) { color = 'rgba(100,149,237,0.8)'; } else if (temperature <= 68) { color = 'rgba(60,179,113,0.8)'; } else if (temperature <= 73) { color = 'rgba(152,251,152,0.8)'; } else if (temperature <= 78) { color = 'rgba(255,223,186,0.8)'; } else if (temperature <= 80) { color = 'rgba(255,215,0,0.8)'; } else if (temperature <= 95) { color = 'rgba(178,34,34,0.8)'; } else { color = 'rgba(139,0,0,0.8)'; } // Apply colour to selected sub-buttons if (elements.sub_buttons) { const subButtons = Array.isArray(elements.sub_buttons) ? elements.sub_buttons : []; subButtons.forEach(num => { if (num >= 1 && num <= 6) { const subButton = card?.querySelector(`.bubble-sub-button-${num} ha-icon`); if (subButton) { subButton.style.color = color; } } }); } // Apply colour to state display if enabled if (elements.state_display) { const stateElement = card?.querySelector('.bubble-state.state.display-state'); if (stateElement) { stateElement.style.color = color; } } return ''; // No CSS needed as we're applying styles directly to elements })()} editor: - type: expandable title: "Entity Configuration" icon: "mdi:thermometer" schema: - name: temperature_sensor label: "Temperature Sensor (Required)" selector: entity: device_class: temperature required: true - name: elements type: grid schema: - name: sub_buttons label: "Sub-buttons to Style" selector: select: multiple: true options: - label: "Sub-button 1" value: 1 - label: "Sub-button 2" value: 2 - label: "Sub-button 3" value: 3 - label: "Sub-button 4" value: 4 - label: "Sub-button 5" value: 5 - label: "Sub-button 6" value: 6 - name: state_display label: "State Display" selector: boolean: {}