Skip to content

Commit

Permalink
Add option to add views to the info box
Browse files Browse the repository at this point in the history
  • Loading branch information
j-a-n committed Dec 15, 2024
1 parent 231841a commit 572c254
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 47 deletions.
57 changes: 46 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ You can set the following configuration parameters for every individual Home Ass
| style | Additional CSS styles for wallpanel elements. | {} |
| badges | Badges to display in info box. Set to [] to show no badges at all. See below for details. | [] |
| cards | Cards to display in info box. Set to [] to show no cards at all. See below for details. | See below |
| card_interaction | Allow interaction with the cards displayed in the info box? | false |
| views | Dashboard views to display in info box. Set to [] to show no views at all. See below for details. | [] |
| card_interaction | Allow interaction with the elements displayed in the info box? | false |
| profiles | Configuration profiles. See below for details. | {} |
| profile | Configuration profile to activate. If browser_mod is installed, `${browser_id}` will be replaced with Browser ID (see below). | |
| profile_entity | An entity of type 'input_text' used for dynamic activation of profiles. If browser_mod is installed, `${browser_id}` will be replaced with Browser ID (see below). | |
Expand Down Expand Up @@ -403,13 +404,33 @@ image_info_template: >-
The CSS class `wallpanel-screensaver-image-info` can be used to style the image info.
See section "Styles".

## Badges and cards
## Info box content
A so-called info box can be displayed above the images.
You can add badges and cards to this box.
You can add badges, cards and whole dashboard views to this box.

You can use the same yaml config, as used in the Home Assistant Dashboard configuration (raw config).
If you want to interact with the elements of the info box, as in the dashboard, you can set `card_interaction` to `true`.

Example (and default) for cards:
```yaml
wallpanel:
card_interaction: true
```

### Badges
For [Badges](https://www.home-assistant.io/dashboards/badges/) you can use the same yaml config,
as used in the Home Assistant Dashboard configuration (raw config).

Example for Badges:
```yaml
wallpanel:
badges:
- entity: person.somebody
- entity: sun.sun
```

### Cards
For [Cards](https://www.home-assistant.io/dashboards/cards/) you can also use the same yaml config.

Example (and default) for Cards:
```yaml
wallpanel:
cards:
Expand All @@ -419,20 +440,33 @@ wallpanel:
forecast_type: daily
```

If you want to interact with the cards, as in the dashboard, you can set `card_interaction` to `true`.
### Views
You can also display entire Dashboard Views in the info box.
The [Views or Subviews](https://www.home-assistant.io/dashboards/views/) you want to use can be referenced by their `path` or `title`.

For example, you can create a subview in your dashboard with the title `Wallpanel`.
You can then reference the view in the wallpanel configuration:


```yaml
wallpanel:
card_interaction: true
views:
- title: Wallpanel
```

Example for badges:
The width of the view can be adjusted using CSS.
The following example uses 80% of the available width of the viewport:

```yaml
wallpanel:
badges:
- entity: person.somebody
- entity: sun.sun
views:
- title: Wallpanel
style:
wallpanel-screensaver-info-box-views:
width: 80vw
```


## Info box animation
The info box, which contains the cards and badges, can be animated and moved around the screen using CSS animations.
The following settings can be used to configure the animation:
Expand Down Expand Up @@ -504,6 +538,7 @@ The most important element IDs are:
- `wallpanel-screensaver-info-box`
- `wallpanel-screensaver-info-box-content`
- `wallpanel-screensaver-info-box-badges`
- `wallpanel-screensaver-info-box-views`
- `wallpanel-screensaver-image-background`
- `wallpanel-screensaver-image-info`
- `wallpanel-screensaver-image-overlay`
Expand Down
157 changes: 121 additions & 36 deletions wallpanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ class CameraMotionDetection {
this.height = 48;
this.threshold = this.width * this.height * 0.05;
this.captureInterval = 300;

this.videoElement = document.createElement("video");
this.videoElement.setAttribute("id", "wallpanelMotionDetectionVideo");
this.videoElement.style.display = 'none';
document.body.appendChild(this.videoElement);

this.canvasElement = document.createElement("canvas");
this.canvasElement.setAttribute("id", "wallpanelMotionDetectionCanvas");
this.canvasElement.style.display = 'none';
Expand Down Expand Up @@ -159,7 +159,7 @@ class CameraMotionDetection {
logger.error("No media devices found");
return;
}

this.enabled = true;
this.width = config.camera_motion_detection_capture_width;
this.height = config.camera_motion_detection_capture_height;
Expand Down Expand Up @@ -207,7 +207,7 @@ class CameraMotionDetection {
}
}

const version = "4.31.2";
const version = "4.32.0";
const defaultConfig = {
enabled: false,
enabled_on_tabs: [],
Expand Down Expand Up @@ -276,6 +276,7 @@ const defaultConfig = {
cards: [
{type: 'weather-forecast', entity: 'weather.home', show_forecast: true}
],
views: [],
card_interaction: false,
profile: '',
profile_entity: '',
Expand Down Expand Up @@ -833,6 +834,7 @@ class WallpanelView extends HuiView {
this.__hass = elHass.__hass;
this.__cards = [];
this.__badges = [];
this.__views = [];

elHass.provideHass(this);
setInterval(this.timer.bind(this), 1000);
Expand Down Expand Up @@ -881,6 +883,9 @@ class WallpanelView extends HuiView {
this.__badges.forEach(badge => {
badge.hass = this.hass;
});
this.__views.forEach(view => {
view.hass = this.hass;
});
}
}

Expand Down Expand Up @@ -1081,26 +1086,39 @@ class WallpanelView extends HuiView {
this.infoContainer.style.height = '100%';
this.infoContainer.style.transition = 'opacity 2000ms ease-in-out';
this.infoContainer.style.padding = '25px';

this.fixedInfoContainer.removeAttribute('style');
this.fixedInfoContainer.style.position = 'fixed';
this.fixedInfoContainer.style.pointerEvents = 'none';
this.fixedInfoContainer.style.top = 0;
this.fixedInfoContainer.style.left = 0;
this.fixedInfoContainer.style.width = '100%';
this.fixedInfoContainer.style.height = '100%';
this.infoContainer.style.boxSizing = 'border-box';

this.infoBox.removeAttribute('style');
this.infoBox.style.pointerEvents = 'none';
this.infoBox.style.width = 'fit-content';
this.infoBox.style.height = 'fit-content';
this.infoBox.style.maxHeight = '100%';
this.infoBox.style.borderRadius = '10px';
this.infoBox.style.overflowY = "auto";
this.infoBox.style.scrollbarWidth = "none";
this.infoBox.style.setProperty('--wp-card-width', '500px');
this.infoBox.style.setProperty('--wp-card-padding', '0');
this.infoBox.style.setProperty('--wp-card-margin', '5px');
this.infoBox.style.setProperty('--wp-card-backdrop-filter', 'none');
this.infoBox.style.setProperty('--wp-badges-minwidth', '200px');

this.infoBoxPosX.style.height = '100%';
this.infoBoxPosX.style.width = '100%';

this.infoBoxPosY.style.height = '100%';
this.infoBoxPosY.style.width = '100%';

this.infoBoxContent.style.width = 'fit-content';
this.infoBoxContent.style.height = '100%';
this.infoBoxContent.style.display = 'grid';

this.fixedInfoContainer.removeAttribute('style');
this.fixedInfoContainer.style.position = 'fixed';
this.fixedInfoContainer.style.pointerEvents = 'none';
this.fixedInfoContainer.style.top = 0;
this.fixedInfoContainer.style.left = 0;
this.fixedInfoContainer.style.width = '100%';
this.fixedInfoContainer.style.height = '100%';

this.fixedInfoBox.style.cssText = this.infoBox.style.cssText;
this.fixedInfoBox.style.pointerEvents = 'none';

Expand Down Expand Up @@ -1142,7 +1160,13 @@ class WallpanelView extends HuiView {

if (config.style) {
for (const elId in config.style) {
if (elId.startsWith('wallpanel-') && elId != 'wallpanel-shadow-host' && elId != 'wallpanel-screensaver-info-box-badges' && !classStyles[elId]) {
if (
elId.startsWith('wallpanel-') &&
elId != 'wallpanel-shadow-host' &&
elId != 'wallpanel-screensaver-info-box-badges' &&
elId != 'wallpanel-screensaver-info-box-views' &&
!classStyles[elId]
) {
let el = this.shadowRoot.getElementById(elId);
if (el) {
logger.debug(`Setting style attributes for element #${elId}`);
Expand All @@ -1167,8 +1191,8 @@ class WallpanelView extends HuiView {

updateShadowStyle() {
let computed = getComputedStyle(this.infoContainer);
let maxX = this.infoContainer.offsetWidth - parseInt(computed.paddingLeft) * 2 - parseInt(computed.paddingRight) * 2 - this.infoBox.offsetWidth;
let maxY = this.infoContainer.offsetHeight - parseInt(computed.paddingTop) * 2 - parseInt(computed.paddingBottom) * 2 - this.infoBox.offsetHeight;
let maxX = this.infoContainer.offsetWidth - parseInt(computed.paddingLeft) - parseInt(computed.paddingRight) - this.infoBox.offsetWidth;
let maxY = this.infoContainer.offsetHeight - parseInt(computed.paddingTop) - parseInt(computed.paddingBottom) - this.infoBox.offsetHeight;
let host = '';

if (config.style) {
Expand Down Expand Up @@ -1230,8 +1254,8 @@ class WallpanelView extends HuiView {

randomMove() {
let computed = getComputedStyle(this.infoContainer);
let maxX = this.infoContainer.offsetWidth - parseInt(computed.paddingLeft) * 2 - parseInt(computed.paddingRight) * 2 - this.infoBox.offsetWidth;
let maxY = this.infoContainer.offsetHeight - parseInt(computed.paddingTop) * 2 - parseInt(computed.paddingBottom) * 2 - this.infoBox.offsetHeight;
let maxX = this.infoContainer.offsetWidth - parseInt(computed.paddingLeft) - parseInt(computed.paddingRight) - this.infoBox.offsetWidth;
let maxY = this.infoContainer.offsetHeight - parseInt(computed.paddingTop) - parseInt(computed.paddingBottom) - this.infoBox.offsetHeight;
let x = Math.floor(Math.random() * maxX);
let y = Math.floor(Math.random() * maxY);
this.moveInfoBox(x, y);
Expand All @@ -1240,8 +1264,8 @@ class WallpanelView extends HuiView {
moveAroundCorners() {
this.lastCorner = (this.lastCorner + 1) % 4;
let computed = getComputedStyle(this.infoContainer);
let x = [2, 3].includes(this.lastCorner) ? this.infoContainer.offsetWidth - parseInt(computed.paddingLeft) * 2 - parseInt(computed.paddingRight) * 2 - this.infoBox.offsetWidth : 0;
let y = [1, 2].includes(this.lastCorner) ? this.infoContainer.offsetHeight - parseInt(computed.paddingTop) * 2 - parseInt(computed.paddingBottom) * 2 - this.infoBox.offsetHeight : 0;
let x = [2, 3].includes(this.lastCorner) ? this.infoContainer.offsetWidth - parseInt(computed.paddingLeft) - parseInt(computed.paddingRight) - this.infoBox.offsetWidth : 0;
let y = [1, 2].includes(this.lastCorner) ? this.infoContainer.offsetHeight - parseInt(computed.paddingTop) - parseInt(computed.paddingBottom) - this.infoBox.offsetHeight : 0;
this.moveInfoBox(x, y);
}

Expand Down Expand Up @@ -1290,13 +1314,14 @@ class WallpanelView extends HuiView {
this.infoBoxContent.innerHTML = '';
this.__badges = [];
this.__cards = [];
this.__views = [];
this.energyCollectionUpdateEnabled = false;

this.shadowRoot.querySelectorAll(".wp-card").forEach(card => {
card.parentElement.removeChild(card);
})

if (config.badges) {
if (config.badges && config.badges.length > 0) {
const div = document.createElement('div');
div.id = "wallpanel-screensaver-info-box-badges";
div.classList.add("wp-badges");
Expand Down Expand Up @@ -1335,7 +1360,66 @@ class WallpanelView extends HuiView {
});
this.infoBoxContent.appendChild(div);
}
if (config.cards) {

if (config.views && config.views.length > 0) {
const div = document.createElement('div');
div.id = "wallpanel-screensaver-info-box-views";
div.classList.add("wp-views");
if (config.style[div.id]) {
for (const attr in config.style[div.id]) {
logger.debug(`Setting style attribute ${attr} to ${config.style[div.id][attr]}`);
div.style.setProperty(attr, config.style[div.id][attr]);
}
}

const viewConfigs = this.lovelace.config.views;
config.views.forEach(view => {
let viewIndex = -1;
let viewConfig = JSON.parse(JSON.stringify(view));
for (var i = 0; i < viewConfigs.length; i++) {
if (
(viewConfigs[i].path && view.path && viewConfigs[i].path.toLowerCase() == view.path.toLowerCase()) ||
(viewConfigs[i].title && view.title && viewConfigs[i].title.toLowerCase() == view.title.toLowerCase())
) {
viewIndex = i;
viewConfig.title = viewConfigs[i].title;
viewConfig.path = viewConfigs[i].path;
break;
}
}
if (viewIndex == -1) {
logger.error(`View with path '${viewConfig.path}' / tile '${viewConfig.title}' not found`);
viewIndex = 0;
}

const viewElement = document.createElement('hui-view');
viewElement.route = {prefix: '/' + activePanel, path: '/' + view.path};
viewElement.lovelace = this.lovelace;
viewElement.panel = this.hass.panels[activePanel];
viewElement.hass = this.hass;
viewElement.index = viewIndex;
if (typeof viewConfig.narrow == "boolean") {
viewElement.narrow = viewConfig.narrow;
}
this.__views.push(viewElement);

const viewContainer = document.createElement('div');
if (config.card_interaction) {
viewElement.style.pointerEvents = "initial";
}
if (viewConfig.wp_style) {
for (const attr in viewConfig.wp_style) {
viewContainer.style.setProperty(attr, viewConfig.wp_style[attr]);
}
}

viewContainer.append(viewElement);
div.append(viewContainer);
});
this.infoBoxContent.appendChild(div);
}

if (config.cards && config.cards.length > 0) {
config.cards.forEach(card => {
// Copy object
let cardConfig = JSON.parse(JSON.stringify(card));
Expand All @@ -1349,19 +1433,23 @@ class WallpanelView extends HuiView {
cardConfig.collection_key = "energy_wallpanel";
this.energyCollectionUpdateEnabled = true;
}

const createCardElement = this._createCardElement ? this._createCardElement : this.createCardElement;
const cardElement = createCardElement.bind(this)(cardConfig);
cardElement.hass = this.hass;

this.__cards.push(cardElement);

let parent = this.infoBoxContent;
const div = document.createElement('div');
div.classList.add("wp-card");
div.style.width = 'var(--wp-card-width)';
div.style.padding = 'var(--wp-card-padding)';
div.style.margin = 'var(--wp-card-margin)';
div.style.backdropFilter = 'var(--wp-card-backdrop-filter)';
const cardContainer = document.createElement('div');
cardContainer.classList.add("wp-card");
cardContainer.style.width = 'var(--wp-card-width)';
cardContainer.style.padding = 'var(--wp-card-padding)';
cardContainer.style.margin = 'var(--wp-card-margin)';
cardContainer.style.backdropFilter = 'var(--wp-card-backdrop-filter)';

if (config.card_interaction) {
cardContainer.style.pointerEvents = "initial";
}
for (const attr in style) {
if (attr == "parent") {
let pel = this.shadowRoot.getElementById(style[attr]);
Expand All @@ -1370,14 +1458,12 @@ class WallpanelView extends HuiView {
}
}
else {
div.style.setProperty(attr, style[attr]);
cardContainer.style.setProperty(attr, style[attr]);
}
}
if (config.card_interaction) {
div.style.pointerEvents = "initial";
}
div.append(cardElement);
parent.appendChild(div);

cardContainer.append(cardElement);
parent.appendChild(cardContainer);
});
}

Expand Down Expand Up @@ -4001,4 +4087,3 @@ EXIF.pretty = function(img) {
EXIF.readFromBinaryFile = function(file) {
return findEXIFinJPEG(file);
}

0 comments on commit 572c254

Please sign in to comment.