Skip to content

Commit

Permalink
Merge pull request #6 from Sian-Lee-SA/dev
Browse files Browse the repository at this point in the history
New inheritance system
  • Loading branch information
Sian-Lee-SA authored Apr 30, 2020
2 parents 6b56dd6 + eb1ed8e commit 211110b
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 66 deletions.
72 changes: 66 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The module uses a hierarchy override for honeycomb options and sub options so yo

## Requirements
1. [Card Tools](https://github.com/thomasloven/lovelace-card-tools)
1. [button-card](https://github.com/custom-cards/button-card)

## How to install
1. Download the [module](https://github.com/Sian-Lee-SA/honeycomb-menu/releases)
Expand Down Expand Up @@ -55,8 +56,8 @@ Option | Values | Default | Details
-- | - |- |-
action | `'tap' \| 'hold' \| 'double_tap'` | `hold` | Define the action that will activate the honeycomb menu (the action is bound to the card). It maybe wise to ensure this action doesn't bubble that will execute the cards default action; so for [custom:button-card](https://github.com/custom-cards/button-card) just make sure the options for that card doesn't conflict with same action that opens honeycomb unless you have unique reasons to do so.
entity | `str:entity_id` | `card:entity` | This will call actions on the entity (entity_id) defined. If omitted then the card entity will be used.
template_buttons | _list[0-5]_: [Button](#button-options) `\| 'break'` | `null` | if using template or card options then this will allow the use of both card and template button configs. `break` will disable the honeycomb on the index.
buttons | _list[0-5]_: [Button](#button-options) `\| 'skip' \| 'break'` | `null \| template_buttons` | The buttons are your honeycombs :grinning:. There are a max of 6 buttons that you can define. _* note: list indexes start at `0`_. Matching indexes with **template_buttons** will be overridden. Using the string `skip` on an index will use the `template_button` for that index and the string `break` will instead disable that honeycomb position regardless of the `template_button` value for that index.
template | `str:template_name` | `null` | If templated has been added to your lovelace config, then this options will merge with the menu. The higher up on the hierarchy takes precedence. For more info see [Inheritance](#inheritance).
buttons | _list[0-5]_: [Button](#button-options) `\| 'skip' \| 'break'` | `null` | The buttons are your honeycombs :grinning:. There are a max of 6 buttons that you can define. _* note: list indexes start at `0`_. Matching indexes with inherited buttons will be overridden. Using the string `skip` on an inherited index will use the inherited button for that index. The string `break` will instead disable that honeycomb position regardless any inheritance.
active | `boolean` | `false` | Setting this to true will apply active styles based on the entity it's assigned to
autoclose | `boolean` | `true` | Close the menu if a button is pressed
audio | `str:url_path` | `null` | Point to a audio file that will play when a button has been tapped
Expand All @@ -68,15 +69,16 @@ spacing | `int:px` | `2` | This will assign the padding in px for each honeycomb

Option | Values | Default | Details
-- | - | - | -
type | `any:card` | `custom:button-card` | The base card to use for the button **Be sure to set the underlying card to 100% height or it may not display correctly**
active | `boolean` | `honeycomb:active` | Override the honeycomb active property for this button item
autoclose | `boolean` | `honeycomb:autoclose` | Override the honeycomb autoclose property for this button
audio | `str:url_path` | `honeycomb:audio` | Override the honeycomb audio property for this button
entity | `str:entity_id` | `honeycomb:entity` | You can define the entity that this button targets. Omitted will resort to the honeycombs entity.
icon | `str:icon` | `null` | Only adding here for reference to custom:button-card so you can show an icon for the item
color | `str:css_color` | `var(--honeycomb-menu-icon-color)` | Color of icon or background (depending on custom:button-card config). Leaving the default value allows the theme to handle the color
color | `str:css_color` | `var(--honeycomb-menu-icon-color)` | Color of icon. Leaving the default value allows the theme to handle the color
show_name | `boolean` | `false` | Only relevant for cards that support this option
Any other options for `Button:type` | - | - | -
tap_action | [Action](https://github.com/custom-cards/button-card#action) | `null` | Assign the action to take if this button has been tapped
hold_action | [Action](https://github.com/custom-cards/button-card#action) | `null` | Assign the action to take if this button has been held
double_tap_action | [Action](https://github.com/custom-cards/button-card#action) | `null` | Assign the action to take if this button has been tapped twice quickly

## `XYPad` Options

Expand All @@ -100,6 +102,64 @@ invert | `boolean` | `false` | x or y will swap negative and positive values so
service | `str:service` | `null` | The service to call eg. light.turn_on. If this value is omitted then the ball pin will have no effect on this axis
service_data | `dict` | `null` | Provide any service data as a dictionary / object. This property will be processed through the template system allowing access to variables and javascript. See [Templating](#templating).

## Inheritance

You may add templates for your honeycomb menu to inherit from. A template can also derive from another template and so on. Add `honeycomb_menu_templates` property to your lovelace config.

Edit your lovelace yaml file
```yaml
title: Home Assistant
honeycomb_menu_templates: !include ../honeycomb-menu-templates.yaml
```
To keep things neat and tidy, it's a good idea to include the templates from a separate file like shown above.

Your templates can hold any value that `honeycomb:` accepts. The name of the template needs to be defined with the properties and values assigned to the template name. See below for an example of how to define a template and how the light template also inherits the base template.

The example shows that both light and base have action assigned. The result will be action: hold because base in inherited which in turn gets overridden on matching values. `skip` and `break` are also valid buttons where skip will resort to it's inherited button value.
```yaml
base:
action: tap
light:
template: base
action: hold
audio: /local/audio/pin-drop.ogg
xy_pad:
repeat: 500
y:
invert: true
service: light.relative_brightnesss
service_data:
entity_id: entity
brightness: '[[[ return {{ y_percentage }} / 10; ]]]'
percentage: true
buttons:
- icon: 'mdi:lightbulb'
active: true
- icon: 'mdi:information-variant'
tap_action:
action: more-info
- icon: 'mdi:page-next'
autoclose: false
tap_action:
action: call-service
service: light.cycle_light_profile
service_data:
entity_id: entity
```

Once your templates are done, you can simply inherit them by adding template with the value being your template name to your honeycomb config.

```yaml
type: custom:button-card
entity: light.livingroom_main
template: light
honeycomb:
template: light
```

If using button card or something similar with a template system then you could add honeycomb to the cards template instead. Keep in mind that Custom Button Card assigns their template values. So if adding a honeycomb to a card that has a template, then the honeycomb will assign the values instead of merging and may give undesired results. This is a result of how the developer designs their cards.

## Theme Styles and Defaults

Adding the following style properties to your theme `.yaml` file will override the defaults
Expand All @@ -110,7 +170,7 @@ styes {
--honeycomb-menu-icon-active-color: var(--paper-item-icon-active-color);
--honeycomb-menu-background-color: var(--paper-card-background-color);
--honeycomb-menu-active-background-color: var(--paper-card-active-background-color, var(--paper-card-background-color));
--honeycomb-menu-disabled: #9a9a9a6e
--honeycomb-menu-disabled: #9a9a9a6e;
}
```

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "honeycomb-menu",
"version": "0.8.0",
"version": "0.8.9",
"description": "Display a popup menu for Home Assistant lovelace cards via tap, hold or double tap",
"scripts": {
"build": "webpack",
Expand Down
19 changes: 17 additions & 2 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,26 @@
* @Date: 2020-04-23T02:41:27+09:30
* @Email: [email protected]
* @Filename: helpers.js
* @Last modified by: Sian Croser
* @Last modified time: 2020-04-26T07:33:31+09:30
* @Last modified by: Sian Croser <Sian-Lee-SA>
* @Last modified time: 2020-04-29T05:08:15+09:30
* @License: GPL-3
*/

export function fireEvent( _node, _event, _detail = {}, _options = {})
{
const event = new Event( _event, Object.assign({
bubbles: true,
cancelable: false,
composed: true
}, _options));

event.detail = _detail;

_node.dispatchEvent(event);

return event;
}

export function evalTemplate(hass, state, func)
{
try {
Expand Down
29 changes: 16 additions & 13 deletions src/honeycomb-menu-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
* @Date: 2020-04-19T19:45:08+09:30
* @Email: [email protected]
* @Filename: comb.js
* @Last modified by: Sian Croser
* @Last modified time: 2020-04-27T06:55:33+09:30
* @Last modified by: Sian Croser <Sian-Lee-SA>
* @Last modified time: 2020-04-30T16:16:53+09:30
* @License: GPL-3
*/

var cardTools = customElements.get('card-tools');

class HoneycombMenuItem extends Polymer.Element
{
static get is()
Expand Down Expand Up @@ -49,6 +47,7 @@ class HoneycombMenuItem extends Polymer.Element
return Polymer.html`
<style>
:host {
--paper-item-icon-active-color: var(--paper-item-icon-color);
}
:host([active]) {
--paper-card-background-color: var(--paper-card-active-background-color);
Expand Down Expand Up @@ -124,19 +123,23 @@ class HoneycombMenuItem extends Polymer.Element
active: false
}, this.config);

if( ! this.config.active )
this.style.setProperty('--paper-item-icon-active-color', 'var(--paper-item-icon-color)');

this.$.item.append( this._createLovelaceCard() );
this.$.item.append( this._createItemCard() );
}

_createLovelaceCard()
_createItemCard()
{
var card = cardTools.createCard(_.merge({}, {
var card = cardTools.createCard({
type: 'custom:button-card',
entity: this.config.entity,
size: '30px',
show_name: false
}, this.config));
show_name: false,
icon: this.config.icon,
tap_action: this.config.tap_action,
hold_action: this.config.hold_action,
double_tap_action: this.config.double_tap_action,
confirmation: this.config.confirmation,
color: this.config.color
});
cardTools.provideHass( card );

card.addEventListener('action', e => {
Expand All @@ -145,7 +148,7 @@ class HoneycombMenuItem extends Polymer.Element
e.detail.audio = this.config.audio;
});

var sheet = new CSSStyleSheet
var sheet = new CSSStyleSheet;
sheet.replaceSync( `ha-card { height: 100%; position: fixed !important; }`);
card.shadowRoot.adoptedStyleSheets = [ ...card.shadowRoot.adoptedStyleSheets, sheet ];

Expand Down
70 changes: 43 additions & 27 deletions src/honeycomb-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* @Date: 2020-04-19T12:09:12+09:30
* @Email: [email protected]
* @Filename: honeycomb-menu.js
* @Last modified by: Sian Croser
* @Last modified time: 2020-04-27T15:19:26+09:30
* @Last modified by: Sian Croser <Sian-Lee-SA>
* @Last modified time: 2020-04-30T09:33:00+09:30
* @License: GPL-3
*/

Expand All @@ -13,14 +13,13 @@ const _ = require('lodash');
import EventManager from './event-manager.js';
import "./honeycomb-menu-item.js";
import "./xy-pad.js";
import { objectEvalTemplate } from "./helpers.js";
import { objectEvalTemplate, fireEvent } from "./helpers.js";

// Hook / Hack the HaCard to handle our needs and allow instantiating the hoeycomb
customElements.whenDefined('ha-card').then(() => {
const HaCard = customElements.get('ha-card');
Object.assign( HaCard.prototype, EventManager.prototype );

const cardTools = customElements.get('card-tools');
Object.assign( HaCard.prototype, EventManager.prototype );

_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;

Expand Down Expand Up @@ -64,6 +63,37 @@ customElements.whenDefined('ha-card').then(() => {
if( ! config || ! config.honeycomb )
return;

function traverseConfigs( _config )
{
if( _.isString(_config) )
_config = cardTools.lovelace.config.honeycomb_menu_templates[_config];
// Allow non extensible to be a new object that can be extended. Using
// merge will also affect sub properties
_config = _.merge({}, _config );
// If there are no buttons then we want it defined for returned calculations
if( ! _config.buttons )
_config.buttons = new Array(6);

if( ! _config.template ||
! cardTools.lovelace.config.honeycomb_menu_templates ||
! cardTools.lovelace.config.honeycomb_menu_templates[_config.template]
) return _config;

let pConfig = traverseConfigs( cardTools.lovelace.config.honeycomb_menu_templates[_config.template] );

for( let i = 0; i < 6; i++ )
{
if( ! _config.buttons[i] || _config.buttons[i] == 'skip' )
_config.buttons[i] = pConfig.buttons[i];
}
return Object.assign({}, pConfig, _config );
}

const honeycombConfig = traverseConfigs( config.honeycomb );
if( ! honeycombConfig.entity )
honeycombConfig.entity = config.entity;

// console.dir(honeycombConfig);
// Remove the following listeners that were defined by ActionHandler so to avoid duplicates
this.removeEventListeners(['contextmenu', 'touchstart', 'touchend', 'touchcancel', 'mousedown', 'click', 'keyup']);

Expand All @@ -73,14 +103,14 @@ customElements.whenDefined('ha-card').then(() => {
// regardless of our config action
document.body.querySelector("action-handler").bind(this, {
hasHold: true,
hasDoubleClick: true,
hasDoubleClick: true
});

// Push our action listener to the top of the list so we can
// see if our menu was trigger and to stop propagation if so...
// If not, then the event will follow through to the other listeners
this.prependEventListener('action', e => {
if( e.detail.action != config.honeycomb.action )
if( e.detail.action != honeycombConfig.action )
return;

e.stopImmediatePropagation();
Expand All @@ -92,11 +122,8 @@ customElements.whenDefined('ha-card').then(() => {
manager.honeycomb = document.createElement('honeycomb-menu');
// Some configs can be non extensible so we make them
// extensible
manager.honeycomb.config = Object.create( config.honeycomb );
manager.honeycomb.base = Object.create( config );

manager.honeycomb.config = honeycombConfig;
manager.honeycomb.display( cardTools.lovelace_view(), manager.position.x, manager.position.y );

manager.honeycomb.addEventListener('closing', e => {
manager.honeycomb = null;
});
Expand All @@ -116,7 +143,6 @@ class HoneycombMenu extends Polymer.Element
return {
hass: Object,
config: Object,
base: Object,
sizes: {
type: Object,
readonly: true
Expand Down Expand Up @@ -296,7 +322,7 @@ class HoneycombMenu extends Polymer.Element

_.defaults(this.config, {
action: 'hold',
entity: this.base.entity,
entity: null,
active: false,
autoclose: true,
size: 225,
Expand Down Expand Up @@ -326,7 +352,7 @@ class HoneycombMenu extends Polymer.Element
this._setCssVars();
}

close( _item )
close( _item = null )
{
if( this.closing )
return;
Expand All @@ -335,20 +361,14 @@ class HoneycombMenu extends Polymer.Element

if( _item ) _item.setAttribute('selected', '');

var details = {
detail: {
item: _item || false
}
};

this.dispatchEvent( new CustomEvent('closing', details) );
fireEvent(this, 'closing', { item: _item });
// Remove shade div earlier to allow clicking of other lovelace elements while the animation continues
this.$.shade.addEventListener('animationend', function(e) {
this.remove();
});
this.shadowRoot.querySelectorAll('honeycomb-menu-item')[5].addEventListener('animationend', e => {
this.remove();
this.dispatchEvent( new CustomEvent('closed', details) );
fireEvent(this, 'closed', { item: _item });
});
}

Expand All @@ -359,10 +379,7 @@ class HoneycombMenu extends Polymer.Element
{
let button = {};

if( this.config.template_buttons && this.config.template_buttons[i] )
button = this.config.template_buttons[i];

if( this.config.buttons && this.config.buttons[i] && this.config.buttons[i] != 'skip' )
if( this.config.buttons && this.config.buttons[i] )
button = this.config.buttons[i];

if( button == 'break' )
Expand Down Expand Up @@ -495,7 +512,6 @@ class HoneycombMenu extends Polymer.Element
{
if( _.isEmpty(item) )
return item;

return _.omit( _.merge( {}, this.config, item ), ['buttons', 'size', 'action', 'template_buttons', 'xy_pad', 'spacing'] );
}

Expand Down
Loading

0 comments on commit 211110b

Please sign in to comment.