[SOLVED] Panasonic_bluray Integration - SKIPREV, SKIPFWD, and OP_CL incorporated (CD PLAYER FUNCTIONALITY)

I have been playing around with panacotta and realized there are a ton of commands that are not incorporated? I am just learning python, so I am not sure how to modify the if/else statements to make everything work, but it seems that the following could be added to the PanasonicDB class section:

        | MediaPlayerEntityFeature.SKIPFWD
        | MediaPlayerEntityFeature.SKIPPREV
        | MediaPlayerEntityFeature.OP_CL
    )

and the following at the end:

    def media_prev(self) -> None:
        """Send prev command."""
        self._device.send_key("SKIPPREV")

    def media_next(self) -> None:
        """Send next command."""
        self._device.send_key("SKIPFWD")

    def media_eject(self) -> None:
        """Send eject command."""
        self._device.send_key("OP_CL")

to add previous/next/eject functionality? Curious why it was not added?

I tried to find the media_python.py file by nosing around in SSH, but am not having any success. I assume it is in a container (I do not know docker, so I have no clue how that works.)?

I am very interested in playing around with this, but first and foremost I think it would be a benefit to the integration if the added functions were added? I know there are not many users using it, but I think it would be a quick addition for someone that knows python better than me? :wink:

Given the lack of a codeowner for panasonic_bluray you may be doing this yourself.

Also you may be better on the panacotta GitHub, this is the first thing to do.

I have panacotta working fine in a linux VM on my laptop… Most of the commands working. I can not find where the media_player.py module resides on my HA install. I found the file on github and understand maybe 85% of the code. When I have time I am going to stick it on the VM I installed panacotta in and see if I can figure the rest of it out, and then make the modifications I suggested.

I am not a programmer so github use is about as mysterious as python was before I spent a couple years learning data types and beginning to understand the builtin functions. but If I can figure out how to get this working I will see if I can figure out how to make a feature submission on github. :+1:

Making some progress… So I figured out how to get into the container and modify the media_player.py by dropping to a terminal and entering:

docker exec -it homeassistant bash

then I change to the panasonic_bluray directory, backup the file, and modify the file:

cd /usr/src/homeassistant/homessistant/components/panasonic_bluray/
cp media_player.py media_player.bu
vi media_player.py

I then changed the media_player.py code to the following:

"""Support for Panasonic Blu-ray players."""

from __future__ import annotations

from datetime import timedelta

from panacotta import PanasonicBD
import voluptuous as vol

from homeassistant.components.media_player import (
    PLATFORM_SCHEMA,
    MediaPlayerEntity,
    MediaPlayerEntityFeature,
    MediaPlayerState,
)
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.dt import utcnow

DEFAULT_NAME = "Panasonic Blu-Ray"

SCAN_INTERVAL = timedelta(seconds=30)


PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Required(CONF_HOST): cv.string,
        vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    }
)


def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Panasonic Blu-ray platform."""
    conf = discovery_info if discovery_info else config

    # Register configured device with Home Assistant.
    add_entities([PanasonicBluRay(conf[CONF_HOST], conf[CONF_NAME])])


class PanasonicBluRay(MediaPlayerEntity):
    """Representation of a Panasonic Blu-ray device."""

    _attr_icon = "mdi:disc-player"
    _attr_supported_features = (
        MediaPlayerEntityFeature.TURN_ON
        | MediaPlayerEntityFeature.TURN_OFF
        | MediaPlayerEntityFeature.PLAY
        | MediaPlayerEntityFeature.STOP
        | MediaPlayerEntityFeature.PAUSE
        | MediaPlayerEntityFeature.PREVIOUS_TRACK
        | MediaPlayerEntityFeature.NEXT_TRACK
    )

    def __init__(self, ip, name):
        """Initialize the Panasonic Blue-ray device."""
        self._device = PanasonicBD(ip)
        self._attr_name = name
        self._attr_state = MediaPlayerState.OFF
        self._attr_media_position = 0
        self._attr_media_duration = 0

    def update(self) -> None:
        """Update the internal state by querying the device."""
        # This can take 5+ seconds to complete
        state = self._device.get_play_status()

        if state[0] == "error":
            self._attr_state = None
        elif state[0] in ["off", "standby"]:
            # We map both of these to off. If it's really off we can't
            # turn it on, but from standby we can go to idle by pressing
            # POWER.
            self._attr_state = MediaPlayerState.OFF
        elif state[0] in ["paused", "stopped"]:
            self._attr_state = MediaPlayerState.IDLE
        elif state[0] == "playing":
            self._attr_state = MediaPlayerState.PLAYING

        # Update our current media position + length
        if state[1] >= 0:
            self._attr_media_position = state[1]
        else:
            self._attr_media_position = 0
        self._attr_media_position_updated_at = utcnow()
        self._attr_media_duration = state[2]

    def turn_off(self) -> None:
        """Instruct the device to turn standby.

        Sending the "POWER" button will turn the device to standby - there
        is no way to turn it completely off remotely. However this works in
        our favour as it means the device is still accepting commands and we
        can thus turn it back on when desired.
        """
        if self.state != MediaPlayerState.OFF:
            self._device.send_key("POWER")

        self._attr_state = MediaPlayerState.OFF

    def turn_on(self) -> None:
        """Wake the device back up from standby."""
        if self.state == MediaPlayerState.OFF:
            self._device.send_key("POWER")

        self._attr_state = MediaPlayerState.IDLE

    def media_play(self) -> None:
        """Send play command."""
        self._device.send_key("PLAYBACK")

    def media_pause(self) -> None:
        """Send pause command."""
        self._device.send_key("PAUSE")

    def media_stop(self) -> None:
        """Send stop command."""
        self._device.send_key("STOP")

    def media_previous_track(self) -> None:
        """Send prev command."""
        self._device.send_key("SKIPREV")

    def media_next_track(self) -> None:
        """Send next command."""
        self._device.send_key("SKIPFWD")

The NEXT_TRACK feature now works! PREVIOUS_TRACK works, but when you get past 1 second, you have to trigger it twice. I can not figure out how to get OP_CL (Eject) to work, even though the function syntax is identical, but the service does not appear in the UI. If I add the OP_CL function it will throw an error…

If I check configuration in developer tools after adding the OP_CL code it does not error immediately, but I believe it is because HA has imported the original media_player.py into memory and it is not seeing the edit…
However, after I reboot, and check configuration from developer tools I get the following error:


Configuration warnings
Platform error 'media_player' from integration 'panasonic_bluray' - Exception importing homeassistant.components.panasonic_bluray.media_player

I believe there is a module check somewhere that does not like my additions. I have not added any logic to call the additional functions defined, so I don’t really understand why it errors. I have very basic python experience, so I still do not understand how this module gets called, and I definitely have not inspected all the other imports or completely understand the media_players.py modules code. I hope to learn more as I nose around.

restoring the backed up media_player.py, and rebooting gets rid of the error, even with the other files in the directory:

cp media_player.py media_player.mine
cp media_player.bu media_player.py

I am learning a lot, but without a programmers background I am afraid I am just going to be reaching for straws?! I have looked at the panacotta repo, and it has been a year since any edits, and there is minimal info. If anyone has any pointers or links to articles that might help me learn faster, I would appreciate it. :wink::v:

I HAVE IT WORKING for what I need! :tada: I was wanting a way to use the CD Player (yes I know obsolete technology for some, but I have hundreds of CDs, and the quality is much better than MP3s, so I still enjoy listening to them.) In any case here are the steps I went thru. They maybe advanced for some. It was a learning experience for me, but just make sure you have a backup and enjoy the playtime! :wink:

First enable Remote Connections in your bluray Network Settings?

I have an older DMP-BDT220 (which is supported), but I was beating my head against the wall until I figured out i had to change the Remote setting. I hope that helps.

Get Panacotta on a linux machine https://pypi.org/project/panacotta/

pip install panacotta

This will allow you to test from a linux box running python3 to see what your bluray supports.

This is a python script (panacotta_test.py) I wrote to test the functionality of my player:
#NOTE: be sure to put your device IP ADDRESS in at line 4

#!/usr/bin/python3 
import panacotta

bluray = panacotta.PanasonicBD('192.168.0.14')   # Change to your Device IP Address

KEYS = """

       ['POWER', 'OP_CL',
        'PLAYBACK', 'PAUSE', 'STOP', 'SKIPFWD', 'SKIPREV', 'REV',
        'D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D12',
        'SHARP',  # '#'
        'CLEAR',  # '*' /  CANCEL
        'UP', 'DOWN', 'LEFT', 'RIGHT', 'SELECT', 'RETURN', 'EXIT',
        'MLTNAVI',          # HOME
        'DSPSEL',           # STATUS
        'TITLE', 'MENU', 'PUPMENU',
        'SHFWD1', 'SHFWD2', 'SHFWD3', 'SHFWD4', 'SHFWD5',
        'SHREV1', 'SHREV2', 'SHREV3', 'SHREV4', 'SHREV5',
        'JLEFT', 'JRIGHT',
        'RED', 'BLUE', 'GREEN', 'YELLOW',
        'NETFLIX', 'SKYPE', 'V_CAST', '3D', 'NETWORK', 'AUDIOSEL',
        'KEYS', 'CUE', 'CHROMA',
        'MNBACK', 'MNSKIP', '2NDARY', 'PICTMD', 'DETAIL', 'RESOLUTN',
        # Playback view?
        'OSDONOFF', 'P_IN_P',
        ]

        'Q' to quit!

"""
while True:
    print("Device is currently %s" % bluray.get_play_status()[0])
    print(f"Available KEYS = {KEYS}")
    inp = input("Enter KEY :").upper()
    if inp:
        inp = inp
        if inp == "Q":
             break
        else:
           print(f"executing bluray.send_key('{inp}')...")
           print(bluray.send_key(inp))

Once you confirm your panasonic device supports PLAYBACK, PAUSE, OP_CL, SKIPREV, and SKIPFWD…

If you have not done so already, you need to have the panasonic_bluray integration manually installed: https://www.home-assistant.io/integrations/panasonic_bluray

Then SSH or Terminal into your HA instance…

docker exec -it homeassistant bash

cd /usr/src/homeassistant/homeassistant/components/panasonic_bluray/
cp media_player.py media_player.bu
vi media_player.py

change the media_player.py code to the following:

"""Support for Panasonic Blu-ray players."""

from __future__ import annotations

from datetime import timedelta

from panacotta import PanasonicBD
import voluptuous as vol

from homeassistant.components.media_player import (
    PLATFORM_SCHEMA,
    MediaPlayerEntity,
    MediaPlayerEntityFeature,
    MediaPlayerState,
)
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.dt import utcnow

DEFAULT_NAME = "Panasonic Blu-Ray"

SCAN_INTERVAL = timedelta(seconds=30)


PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Required(CONF_HOST): cv.string,
        vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    }
)


def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Panasonic Blu-ray platform."""
    conf = discovery_info if discovery_info else config

    # Register configured device with Home Assistant.
    add_entities([PanasonicBluRay(conf[CONF_HOST], conf[CONF_NAME])])


class PanasonicBluRay(MediaPlayerEntity):
    """Representation of a Panasonic Blu-ray device."""

    _attr_icon = "mdi:disc-player"
    _attr_supported_features = (
        MediaPlayerEntityFeature.TURN_ON
        | MediaPlayerEntityFeature.TURN_OFF
        | MediaPlayerEntityFeature.PLAY
        | MediaPlayerEntityFeature.STOP
        | MediaPlayerEntityFeature.PAUSE
        | MediaPlayerEntityFeature.PREVIOUS_TRACK
        | MediaPlayerEntityFeature.NEXT_TRACK
    )

    def __init__(self, ip, name):
        """Initialize the Panasonic Blue-ray device."""
        self._device = PanasonicBD(ip)
        self._attr_name = name
        self._attr_state = MediaPlayerState.OFF
        self._attr_media_position = 0
        self._attr_media_duration = 0

    def update(self) -> None:
        """Update the internal state by querying the device."""
        # This can take 5+ seconds to complete
        state = self._device.get_play_status()

        if state[0] == "error":
            self._attr_state = None
        elif state[0] in ["off", "standby"]:
            # We map both of these to off. If it's really off we can't
            # turn it on, but from standby we can go to idle by pressing
            # POWER.
            self._attr_state = MediaPlayerState.OFF
        elif state[0] in ["paused", "stopped"]:
            self._attr_state = MediaPlayerState.IDLE
        elif state[0] == "playing":
            self._attr_state = MediaPlayerState.PLAYING

        # Update our current media position + length
        if state[1] >= 0:
            self._attr_media_position = state[1]
        else:
            self._attr_media_position = 0
        self._attr_media_position_updated_at = utcnow()
        self._attr_media_duration = state[2]

    def turn_off(self) -> None:
        """Instruct the device to turn standby.

        Sending the "POWER" button will turn the device to standby - there
        is no way to turn it completely off remotely. However this works in
        our favour as it means the device is still accepting commands and we
        can thus turn it back on when desired.
        """
        if self.state != MediaPlayerState.OFF:
            self._device.send_key("POWER")

        self._attr_state = MediaPlayerState.OFF

    def turn_on(self) -> None:
        """Wake the device back up from standby."""
        if self.state == MediaPlayerState.OFF:
            self._device.send_key("POWER")

        self._attr_state = MediaPlayerState.IDLE

    def media_play(self) -> None:
        """Send play command."""
        self._device.send_key("PLAYBACK")

    def media_pause(self) -> None:
        """Send pause command."""
        self._device.send_key("PAUSE")

    def media_stop(self) -> None:
        """Send stop command."""
        self._device.send_key("OP_CL")

    def media_previous_track(self) -> None:
        """Send prev command."""
        self._device.send_key("SKIPPREV")

    def media_next_track(self) -> None:
        """Send next command."""
        self._device.send_key("SKIPFWD")

REBOOT HA (Restarting just the YAML will not work.)

NOTE: the STOP service will now eject the CD. Use Pause/Play instead of stop.

I just have a really basic CD tab using the new sections (drag-n-drop) UI with mushroom cards (HACS) - my panasonic device is ‘pan’ so you will need to change all the ‘media_player.pan’ items to your entity:

type: sections
sections:
  - type: grid
    cards:
      - type: custom:mushroom-media-player-card
        entity: media_player.pan
        media_controls:
          - on_off
          - previous
          - play_pause_stop
          - next
          - repeat
          - shuffle
        use_media_info: true
        double_tap_action:
          action: more-info
        name: Pan
        collapsible_controls: false
        secondary_info: state
        fill_container: false
      - type: custom:mushroom-template-card
        primary: Eject
        icon: mdi:eject
        tap_action:
          action: call-service
          service: media_player.media_stop
          target:
            entity_id: media_player.pan
        icon_color: red
      - type: custom:mushroom-template-card
        primary: Play/Pause
        icon: mdi:play-pause
        tap_action:
          action: call-service
          service: media_player.media_play_pause
          target:
            entity_id: media_player.pan
        icon_color: green
      - type: custom:mushroom-template-card
        primary: Prev
        icon: mdi:skip-previous
        tap_action:
          action: call-service
          service: media_player.media_previous_track
          target:
            entity_id:
              - media_player.pan
        icon_color: cyan
      - type: custom:mushroom-template-card
        primary: Next
        icon: mdi:skip-next
        tap_action:
          action: call-service
          service: media_player.media_next_track
          target:
            entity_id:
              - media_player.pan
        icon_color: cyan
    title: Panasonic
cards: []
max_columns: 4
title: CD

I hope this helps others wanting to start to learn and play with media_player and their panasonic bluray. ENJOY! :v:

1 Like