Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(media_player): add support of media player #270

Merged
merged 14 commits into from
Apr 14, 2022
3 changes: 2 additions & 1 deletion .hass_dev/lovelace-mushroom-showcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ views:
- !include views/chips-view.yaml
- !include views/title-view.yaml
- !include views/update-view.yaml
- !include views/vacuum-view.yaml
- !include views/media-player-view.yaml
- !include views/vacuum-view.yaml
71 changes: 71 additions & 0 deletions .hass_dev/views/media-player-view.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
title: Media
icon: mdi:speaker
cards:
- type: grid
title: Simple
cards:
- type: custom:mushroom-media-player-card
entity: media_player.bedroom
- type: custom:mushroom-media-player-card
entity: media_player.kitchen
- type: custom:mushroom-media-player-card
entity: media_player.living_room
- type: custom:mushroom-media-player-card
entity: media_player.lounge_room
- type: custom:mushroom-media-player-card
entity: media_player.walkman
columns: 2
square: false
- type: grid
title: Controls
cards:
- type: custom:mushroom-media-player-card
entity: media_player.bedroom
show_buttons_control: true
show_volume_control: true
- type: custom:mushroom-media-player-card
entity: media_player.kitchen
show_buttons_control: true
show_volume_control: true
- type: custom:mushroom-media-player-card
entity: media_player.living_room
show_buttons_control: true
show_volume_control: true
- type: custom:mushroom-media-player-card
entity: media_player.lounge_room
show_buttons_control: true
show_volume_control: true
- type: custom:mushroom-media-player-card
entity: media_player.walkman
show_buttons_control: true
show_volume_control: true
- type: custom:mushroom-media-player-card
entity: media_player.kitchen
name: Multiple controls
show_buttons_control: true
show_volume_control: true
- type: vertical-stack
title: Layout
cards:
- type: grid
columns: 2
square: false
cards:
- type: custom:mushroom-media-player-card
entity: media_player.kitchen
show_buttons_control: true
show_volume_control: true
- type: grid
columns: 2
square: false
cards:
- type: custom:mushroom-media-player-card
entity: media_player.bedroom
show_buttons_control: true
show_volume_control: true
layout: "vertical"
- type: custom:mushroom-media-player-card
entity: media_player.kitchen
show_buttons_control: true
show_volume_control: true
layout: "horizontal"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ Different cards are available for differents entities :
- 🧹 [Vacuum card](docs/cards/vacuum.md)

Some cards are on the todo list :
- 🌡 Climate card
- 📺 Media card
- 🌡 Climate card

### Theme customization

Expand Down
24 changes: 24 additions & 0 deletions docs/cards/media-player.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Media Player card

![Media Player light](../images/media-player-light.png)
![Media Player dark](../images/media-player-dark.png)

## Description

A media player card allow you to control a media player entity.

## Configuration variables

All the options are available in the lovelace editor but you can use `yaml` if you want.

| Name | Type | Default | Description |
| :---------------------- | :------ | :---------- | :------------------------------------------------------------------------ |
| `entity` | string | Required | Media Player entity |
| `icon` | string | Optional | Custom icon |
| `name` | string | Optional | Custom name |
| `layout` | string | Optional | Layout of the card. Vertical, horizontal and default layout are supported |
| `show_buttons_control` | boolean | `false` | Show buttons control |
| `show_volume_control` | boolean | `false` | Show volume control |
| `tap_action` | action | `toggle` | Home assistant action to perform on tap |
| `hold_action` | action | `more-info` | Home assistant action to perform on hold |
| `double_tap_action` | action | `more-info` | Home assistant action to perform on double_tap |
Binary file added docs/images/media-player-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/media-player-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/cards/media-player-card/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PREFIX_NAME } from "../../const";

export const MEDIA_PLAYER_CARD_NAME = `${PREFIX_NAME}-media-player-card`;
export const MEDIA_PLAYER_CARD_EDITOR_NAME = `${MEDIA_PLAYER_CARD_NAME}-editor`;
export const MEDIA_PLAYER_ENTITY_DOMAINS = ["media_player"];
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { HomeAssistant } from "custom-card-helpers";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { MEDIA_PLAYER_STATE_PLAYING } from "../../../ha/data/media-player";
import { callService, supportsNext, supportsPause, supportsPlay, supportsPrevious } from "../utils";
import "../../../shared/button-group";
import "../../../shared/button";

@customElement("mushroom-media-player-buttons-control")
export class MediaPlayerButtonsControl extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property({ attribute: false }) public entity!: HassEntity;

@property() public fill: boolean = false;

protected render(): TemplateResult {
return html`
<mushroom-button-group .fill=${this.fill}>
${supportsPrevious(this.entity)
? this._generateButton("mdi:skip-previous", "media_previous_track")
: null}
${supportsPlay(this.entity) && supportsPause(this.entity)
? this._generatePlayPauseButton()
: null}
${supportsNext(this.entity)
? this._generateButton("mdi:skip-next", "media_next_track")
: null}
</mushroom-button-group>
`;
}

_generateButton(iconName: string, serviceName: string): TemplateResult {
return html`
<mushroom-button
.icon=${iconName}
@click=${(e) => callService(e, this.hass, this.entity, serviceName)}
></mushroom-button>
`;
}

_generatePlayPauseButton(): TemplateResult {
const serviceName = "media_play_pause";

if (this.entity.state == MEDIA_PLAYER_STATE_PLAYING) {
return html`
<mushroom-button
icon="mdi:pause"
@click=${(e) => callService(e, this.hass, this.entity, serviceName)}
></mushroom-button>
`;
} else {
return html`
<mushroom-button
icon="mdi:play"
@click=${(e) => callService(e, this.hass, this.entity, serviceName)}
></mushroom-button>
`;
}
}

static get styles(): CSSResultGroup {
return css`
:host {
display: flex;
flex-direction: row;
width: 100%;
}
:host *:not(:last-child) {
margin-right: var(--spacing);
}
.container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.container.fill mushroom-button {
flex: 1;
}
`;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { HomeAssistant } from "custom-card-helpers";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { isAvailable } from "../../../ha/data/entity";
import { MediaPlayerEntity } from "../../../ha/data/media-player";
import { getVolumeLevel } from "../utils";

@customElement("mushroom-media-player-volume-control")
export class MediaPlayerVolumeControl extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property({ attribute: false }) public entity!: MediaPlayerEntity;

onChange(e: CustomEvent<{ value: number }>): void {
const value = e.detail.value;
this.hass.callService("media_player", "volume_set", {
entity_id: this.entity.entity_id,
volume_level: value / 100,
});
}

onCurrentChange(e: CustomEvent<{ value?: number }>): void {
const value = (e.detail?.value || 1) / 100;
this.dispatchEvent(
new CustomEvent("current-change", {
detail: {
value,
},
})
);
}

protected render(): TemplateResult | null {
if (!this.entity) return null;

const value = getVolumeLevel(this.entity);
return html`
<mushroom-slider
.value=${value}
.disabled=${!isAvailable(this.entity)}
.showActive=${true}
.min=${0}
.max=${100}
@change=${this.onChange}
@current-change=${this.onCurrentChange}
/>
`;
}

static get styles(): CSSResultGroup {
return css`
mushroom-slider {
--main-color: rgb(var(--rgb-state-media-player));
--bg-color: rgba(var(--rgb-state-media-player), 0.2);
}
`;
}
}
32 changes: 32 additions & 0 deletions src/cards/media-player-card/media-player-card-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ActionConfig, LovelaceCardConfig } from "custom-card-helpers";
import { assign, boolean, object, optional, string } from "superstruct";
import { actionConfigStruct } from "../../utils/action-struct";
import { baseLovelaceCardConfig } from "../../utils/editor-styles";
import { Layout, layoutStruct } from "../../utils/layout";

export interface MediaPlayerCardConfig extends LovelaceCardConfig {
entity?: string;
name?: string;
icon?: string;
show_buttons_control?: boolean;
show_volume_control?: boolean;
layout?: Layout;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}

export const mediaPlayerCardConfigStruct = assign(
baseLovelaceCardConfig,
object({
entity: optional(string()),
icon: optional(string()),
name: optional(string()),
show_buttons_control: optional(boolean()),
show_volume_control: optional(boolean()),
layout: optional(layoutStruct),
tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct),
})
);
Loading