Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/cards/ha-persistent_notification-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '../components/ha-markdown.js';

import computeStateName from '../common/entity/compute_state_name.js';
import LocalizeMixin from '../mixins/localize-mixin.js';
import computeObjectId from '../common/entity/compute_object_id';

/*
* @appliesMixin LocalizeMixin
Expand Down Expand Up @@ -65,7 +66,9 @@ class HaPersistentNotificationCard extends LocalizeMixin(PolymerElement) {

dismissTap(ev) {
ev.preventDefault();
this.hass.callApi('DELETE', 'states/' + this.stateObj.entity_id);
this.hass.callService('persistent_notification', 'dismiss', {
notification_id: computeObjectId(this.stateObj.entity_id)
});
}
}
customElements.define('ha-persistent_notification-card', HaPersistentNotificationCard);
20 changes: 20 additions & 0 deletions src/data/ws-notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createCollection } from 'home-assistant-js-websocket';

const fetchNotifications = conn => conn.sendMessagePromise({
type: 'persistent_notification/get'
});

const subscribeUpdates = (conn, store) =>
conn.subscribeEvents(
() => fetchNotifications(conn).then(ntf => store.setState(ntf, true)),
'persistent_notifications_updated'
);

export const subscribeNotifications = (conn, onChange) =>
createCollection(
'_ntf',
fetchNotifications,
subscribeUpdates,
conn,
onChange
);
7 changes: 1 addition & 6 deletions src/panels/lovelace/common/compute-notifications.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import computeDomain from '../../../common/entity/compute_domain.js';

const NOTIFICATION_DOMAINS = [
'configurator',
'persistent_notification'
];

export default function computeNotifications(states) {
return Object.keys(states)
.filter(entityId => NOTIFICATION_DOMAINS.includes(computeDomain(entityId)))
.filter(entityId => computeDomain(entityId) === 'configurator')
.map(entityId => states[entityId]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,34 @@ export class HuiConfiguratorNotificationItem extends EventsMixin(LocalizeMixin(P
<hui-notification-item-template>
<span slot="header">[[localize('domain.configurator')]]</span>

<div>[[_getMessage(stateObj)]]</div>
<div>[[_getMessage(notification)]]</div>

<paper-button
slot="actions"
class="primary"
on-click="_handleClick"
>[[_localizeState(stateObj.state)]]</paper-button>
>[[_localizeState(notification.state)]]</paper-button>
</hui-notification-item-template>
`;
}

static get properties() {
return {
hass: Object,
stateObj: Object
notification: Object
};
}

_handleClick() {
this.fire('hass-more-info', { entityId: this.stateObj.entity_id });
this.fire('hass-more-info', { entityId: this.notification.entity_id });
}

_localizeState(state) {
return this.localize(`state.configurator.${state}`);
}

_getMessage(stateObj) {
const friendlyName = stateObj.attributes.friendly_name;
_getMessage(notification) {
const friendlyName = notification.attributes.friendly_name;
return this.localize('ui.notification_drawer.click_to_configure', 'entity', friendlyName);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { PolymerElement } from '@polymer/polymer/polymer-element.js';

import './hui-notification-item.js';

import computeNotifications from '../../common/compute-notifications.js';

import EventsMixin from '../../../../mixins/events-mixin.js';
import LocalizeMixin from '../../../../mixins/localize-mixin.js';

Expand Down Expand Up @@ -105,16 +103,16 @@ export class HuiNotificationDrawer extends EventsMixin(LocalizeMixin(PolymerElem
<paper-icon-button icon="hass:chevron-right" on-click="_closeDrawer"></paper-icon-button>
</app-toolbar>
<div class="notifications">
<template is="dom-if" if="[[!_empty(_entities)]]">
<dom-repeat items="[[_entities]]">
<template is="dom-if" if="[[!_empty(notifications)]]">
<dom-repeat items="[[notifications]]">
<template>
<div class="notification">
<hui-notification-item hass="[[hass]]" state-obj="[[item]]"></hui-notification-item>
<hui-notification-item hass="[[hass]]" notification="[[item]]"></hui-notification-item>
</div>
</template>
</dom-repeat>
</template>
<template is="dom-if" if="[[_empty(_entities)]]">
<template is="dom-if" if="[[_empty(notifications)]]">
<div class="empty">[[localize('ui.notification_drawer.empty')]]<div>
</template>
</div>
Expand All @@ -125,10 +123,6 @@ export class HuiNotificationDrawer extends EventsMixin(LocalizeMixin(PolymerElem
static get properties() {
return {
hass: Object,
_entities: {
type: Array,
computed: '_getEntities(hass.states, hidden)'
},
narrow: {
type: Boolean,
reflectToAttribute: true
Expand All @@ -142,21 +136,21 @@ export class HuiNotificationDrawer extends EventsMixin(LocalizeMixin(PolymerElem
type: Boolean,
value: true,
reflectToAttribute: true
},
notifications: {
type: Array,
value: []
}
};
}

_getEntities(states, hidden) {
return (states && !hidden) ? computeNotifications(states) : [];
}

_closeDrawer(ev) {
ev.stopPropagation();
this.open = false;
}

_empty(entities) {
return entities.length === 0;
_empty(notifications) {
return notifications.length === 0;
}

_openChanged(open) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,27 @@ export class HuiNotificationItem extends PolymerElement {
static get properties() {
return {
hass: Object,
stateObj: {
notification: {
type: Object,
observer: '_stateChanged'
}
};
}

_stateChanged(stateObj) {
_stateChanged(notification) {
if (this.lastChild) {
this.removeChild(this.lastChild);
}

if (!stateObj) return;
if (!notification) return;

const domain = computeDomain(stateObj.entity_id);
const domain = notification.entity_id
? computeDomain(notification.entity_id)
: 'persistent_notification';
const tag = `hui-${domain}-notification-item`;
const el = document.createElement(tag);
el.hass = this.hass;
el.stateObj = stateObj;
el.notification = notification;
this.appendChild(el);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import '@polymer/app-layout/app-toolbar/app-toolbar.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';

import computeNotifications from '../../common/compute-notifications.js';

import EventsMixin from '../../../../mixins/events-mixin.js';

/*
Expand Down Expand Up @@ -36,16 +34,19 @@ export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
}
</style>
<paper-icon-button icon="hass:bell" on-click="_clicked"></paper-icon-button>
<span class="indicator" hidden$="[[!_hasNotifications(hass.states)]]"></span>
<span class="indicator" hidden$="[[!_hasNotifications(notifications)]]"></span>
`;
}

static get properties() {
return {
hass: Object,
notificationsOpen: {
type: Boolean,
notify: true
},
notifications: {
type: Array,
value: []
}
};
}
Expand All @@ -54,8 +55,8 @@ export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
this.notificationsOpen = true;
}

_hasNotifications(states) {
return computeNotifications(states).length > 0;
_hasNotifications(notifications) {
return notifications.length > 0;
}
}
customElements.define('hui-notifications-button', HuiNotificationsButton);
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import '@polymer/paper-icon-button/paper-icon-button.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';

import computeStateName from '../../../../common/entity/compute_state_name.js';

import '../../../../components/ha-markdown.js';
import './hui-notification-item-template.js';

Expand All @@ -18,13 +16,13 @@ export class HuiPersistentNotificationItem extends LocalizeMixin(PolymerElement)
static get template() {
return html`
<hui-notification-item-template>
<span slot="header">[[_computeTitle(stateObj)]]</span>
<span slot="header">[[_computeTitle(notification)]]</span>

<ha-markdown content="[[stateObj.attributes.message]]"></ha-markdown>
<ha-markdown content="[[notification.message]]"></ha-markdown>

<paper-button
slot="actions"
class="primary"
<paper-button
slot="actions"
class="primary"
on-click="_handleDismiss"
>[[localize('ui.card.persistent_notification.dismiss')]]</paper-button>
</hui-notification-item-template>
Expand All @@ -34,16 +32,18 @@ export class HuiPersistentNotificationItem extends LocalizeMixin(PolymerElement)
static get properties() {
return {
hass: Object,
stateObj: Object
notification: Object
};
}

_handleDismiss() {
this.hass.callApi('DELETE', `states/${this.stateObj.entity_id}`);
this.hass.callService('persistent_notification', 'dismiss', {
notification_id: this.notification.notification_id
});
}

_computeTitle(stateObj) {
return (stateObj.attributes.title || computeStateName(stateObj));
_computeTitle(notification) {
return notification.title || notification.notification_id;
}
}
customElements.define(
Expand Down
35 changes: 35 additions & 0 deletions src/panels/lovelace/hui-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ import '../../layouts/ha-app-layout.js';
import '../../components/ha-start-voice-button.js';
import '../../components/ha-icon.js';
import { loadModule, loadCSS, loadJS } from '../../common/dom/load_resource.js';
import { subscribeNotifications } from '../../data/ws-notifications';
import './components/notifications/hui-notification-drawer.js';
import './components/notifications/hui-notifications-button.js';
import './hui-unused-entities.js';
import './hui-view.js';
import debounce from '../../common/util/debounce.js';

import createCardElement from './common/create-card-element.js';
import computeNotifications from './common/compute-notifications';

// CSS and JS should only be imported once. Modules and HTML are safe.
const CSS_CACHE = {};
Expand Down Expand Up @@ -76,6 +78,7 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
<app-route route="[[route]]" pattern="/:view" data="{{routeData}}"></app-route>
<hui-notification-drawer
hass="[[hass]]"
notifications="[[_notifications]]"
open="{{notificationsOpen}}"
narrow="[[narrow]]"
></hui-notification-drawer>
Expand All @@ -87,6 +90,7 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
<hui-notifications-button
hass="[[hass]]"
notifications-open="{{notificationsOpen}}"
notifications="[[_notifications]]"
></hui-notifications-button>
<ha-start-voice-button hass="[[hass]]"></ha-start-voice-button>
<paper-menu-button
Expand Down Expand Up @@ -156,6 +160,16 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
value: false,
},

_persistentNotifications: {
type: Array,
value: []
},

_notifications: {
type: Array,
computed: '_updateNotifications(hass.states, _persistentNotifications)'
},

routeData: Object,
};
}
Expand All @@ -165,6 +179,27 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
this._debouncedConfigChanged = debounce(() => this._selectView(this._curView), 100);
}

connectedCallback() {
super.connectedCallback();
this._unsubNotifications = subscribeNotifications(this.hass.connection, (notifications) => {
this._persistentNotifications = notifications;
});
}

disconnectedCallback() {
super.disconnectedCallback();
if (typeof this._unsubNotifications === 'function') {
this._unsubNotifications();
}
}

_updateNotifications(states, persistent) {
if (!states) return persistent;

const configurator = computeNotifications(states);
return persistent.concat(configurator);
}

_routeChanged(route) {
const views = this.config && this.config.views;
if (route.path === '' && route.prefix === '/lovelace' && views) {
Expand Down