| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- """PJLink2 media_player platform."""
- from __future__ import annotations
- from collections.abc import Callable
- from datetime import timedelta
- import logging
- from typing import Any
- from aiopjlink import (
- PJLink,
- PJLinkException,
- PJLinkProjectorError,
- Power,
- Sources,
- Lamp,
- Information,
- )
- from homeassistant import config_entries, core
- from homeassistant.components.media_player import (
- MediaPlayerEntity,
- MediaPlayerEntityFeature,
- MediaPlayerState,
- PLATFORM_SCHEMA,
- )
- from homeassistant.const import (
- CONF_HOST,
- CONF_PORT,
- CONF_NAME,
- CONF_PASSWORD,
- CONF_TIMEOUT,
- )
- from homeassistant.core import HomeAssistant as HomeAssistantType
- import homeassistant.helpers.config_validation as cv
- from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
- import voluptuous as vol
- from .const import (
- DOMAIN,
- CONF_ENCODING,
- DEFAULT_ENCODING,
- DEFAULT_PORT,
- DEFAULT_TIMEOUT,
- ATTR_PRODUCT_NAME,
- ATTR_MANUFACTURER_NAME,
- ATTR_PROJECTOR_NAME,
- ATTR_RESOLUTION_X,
- ATTR_RESOLUTION_Y,
- ATTR_LAMP_HOURS,
- ProjectorState,
- )
- _LOGGER = logging.getLogger(__name__)
- # Time between updating data from projector
- SCAN_INTERVAL = timedelta(seconds=3)
- PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
- {
- vol.Required(CONF_HOST): cv.string,
- vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
- vol.Optional(CONF_NAME): cv.string,
- vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
- vol.Optional(CONF_PASSWORD): cv.string,
- vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_float,
- }
- )
- async def async_setup_platform(
- hass: HomeAssistantType,
- config: ConfigType,
- async_add_entities: Callable,
- discovery_info: DiscoveryInfoType | None = None,
- ) -> None:
- """Set up the media_player platform."""
- host = config.get(CONF_HOST)
- port = config.get(CONF_PORT)
- password = config.get(CONF_PASSWORD)
- timeout = config.get(CONF_TIMEOUT)
- name = config.get(CONF_NAME)
- pjl = PJLink(host, port, password, timeout)
- devices = [PJLink2MediaPlayer(pjl, name)]
- async_add_entities(devices, update_before_add=False)
- class PJLink2MediaPlayer(MediaPlayerEntity):
- """Representation of a PJLink2 media player."""
- _attr_supported_features = (
- MediaPlayerEntityFeature.TURN_ON
- | MediaPlayerEntityFeature.TURN_OFF
- | MediaPlayerEntityFeature.SELECT_SOURCE
- )
- def __init__(self, pjl, name):
- super().__init__()
- self._projector = pjl
- self.attrs: dict[str, Any] = {}
- self._name = name
- self._state = MediaPlayerState.OFF
- self._available = False
- self._connectionErrorLogged = False
- self._current_source = None
- # PJLink standard inputs. You can modify these to friendly names later.
- # Format is typically "31" for HDMI1, "32" for HDMI2, etc.
- self._source_list = ["11", "12", "21", "22", "31", "32", "33"]
- async def async_will_remove_from_hass(self) -> None:
- """Close connection."""
- await super().async_will_remove_from_hass()
- if self._available:
- try:
- await self._projector.__aexit__(0, 0, 0)
- except (PJLinkException, OSError) as err:
- _LOGGER.error(
- "PJLink2 ERROR when closing connection: %s", repr(err)
- )
- @property
- def name(self) -> str:
- return self._name
- @property
- def unique_id(self) -> str:
- return self._projector._address
- @property
- def available(self) -> bool:
- return self._available
- @property
- def state(self) -> MediaPlayerState:
- return self._state
- @property
- def source(self) -> str | None:
- """Name of the current input source."""
- return self._current_source
- @property
- def source_list(self) -> list[str]:
- """List of available input sources."""
- return self._source_list
- @property
- def extra_state_attributes(self) -> dict[str, Any]:
- """Return the custom PJLink2 attributes."""
- return self.attrs
- async def async_turn_on(self) -> None:
- """Turn the projector on."""
- await Power(self._projector).set(Power.ON)
- self._state = MediaPlayerState.ON
- async def async_turn_off(self) -> None:
- """Turn the projector off."""
- await Power(self._projector).set(Power.OFF)
- self._state = MediaPlayerState.OFF
- async def async_select_source(self, source: str) -> None:
- """Select input source."""
- source_type = source[0]
- source_index = source[1]
- await Sources(self._projector).set(source_type, source_index)
- self._current_source = source
- async def async_update(self) -> None:
- """Update data from projector."""
- try:
- if not self._available:
- await self._projector.__aenter__()
- self._available = True
- info = await Information(self._projector).table()
- self.attrs[ATTR_PRODUCT_NAME] = info["product_name"]
- self.attrs[ATTR_MANUFACTURER_NAME] = info["manufacturer_name"]
- self.attrs[ATTR_PROJECTOR_NAME] = info["projector_name"]
- if self._name == None:
- self._name = info["projector_name"]
- pwr = await Power(self._projector).get()
- if pwr == Power.State.OFF:
- self._state = MediaPlayerState.OFF
- elif pwr == Power.State.ON:
- self._state = MediaPlayerState.ON
- elif pwr in (Power.State.COOLING, Power.State.WARMING):
- self._state = (
- MediaPlayerState.ON
- ) # Keeps UI active during transition
- if pwr == Power.ON:
- res = await Sources(self._projector).resolution()
- self.attrs[ATTR_RESOLUTION_X] = res[0]
- self.attrs[ATTR_RESOLUTION_Y] = res[1]
- self.attrs[ATTR_LAMP_HOURS] = await Lamp(
- self._projector
- ).hours()
- # Fetch current source
- current = await Sources(self._projector).get()
- if isinstance(current, (tuple, list)):
- self._current_source = "".join(map(str, current))
- else:
- self._current_source = str(current)
- else:
- self.attrs.pop(ATTR_RESOLUTION_X, None)
- self.attrs.pop(ATTR_RESOLUTION_Y, None)
- self._current_source = None
- self._connectionErrorLogged = False
- except PJLinkProjectorError:
- self.attrs.pop(ATTR_RESOLUTION_X, None)
- self.attrs.pop(ATTR_RESOLUTION_Y, None)
- except (PJLinkException, OSError) as err:
- if not self._connectionErrorLogged:
- _LOGGER.error("PJLink2 ERROR for %s: %s", self._name, repr(err))
- self._connectionErrorLogged = True
- self._state = MediaPlayerState.OFF
- if self._available:
- self._available = False
- try:
- await self._projector.__aexit__(0, 0, 0)
- except Exception:
- pass
|