Преглед изворни кода

add roadmap + add support for a/v mute and freeze

Noah Vogt пре 2 месеци
родитељ
комит
ccb04a3425

+ 10 - 1
README.md

@@ -44,9 +44,18 @@ media_player:
     encoding: "utf-16"        # encoding for communication (optional, default is utf-8)
     encoding: "utf-16"        # encoding for communication (optional, default is utf-8)
     password: "secret%123"    # password to establish connection (optional)
     password: "secret%123"    # password to establish connection (optional)
     timeout: 1.5              # timeout to establish connection in seconds (optional, default is 4 sec)
     timeout: 1.5              # timeout to establish connection in seconds (optional, default is 4 sec)
-    sources:
+    sources:                  # mapping of the raw source to appriorate names
       "31": "Smart TV"
       "31": "Smart TV"
       "32": "Camera HDMI Out"
       "32": "Camera HDMI Out"
       "11": "Laptop"
       "11": "Laptop"
 ```
 ```
 If you omit the sources block, the integration will show raw codes like 31, 32, etc., and add new ones to the dropdown as you switch to them on the device.
 If you omit the sources block, the integration will show raw codes like 31, 32, etc., and add new ones to the dropdown as you switch to them on the device.
+
+
+## Roadmap
+
+Possible changes in future releases:
+
+- if the sources yaml block was omitted, give the user all options the projector reports support for
+- add the option to only poll for a subset of data
+- support the full pjlink2 spec

+ 6 - 1
custom_components/pjlink2/const.py

@@ -1,4 +1,5 @@
 """Provides the constants needed for component."""
 """Provides the constants needed for component."""
+
 from enum import StrEnum
 from enum import StrEnum
 
 
 DOMAIN = "pjlink2"
 DOMAIN = "pjlink2"
@@ -15,9 +16,13 @@ ATTR_PROJECTOR_NAME = "projector_name"
 ATTR_RESOLUTION_X = "x_resolution"
 ATTR_RESOLUTION_X = "x_resolution"
 ATTR_RESOLUTION_Y = "y_resolution"
 ATTR_RESOLUTION_Y = "y_resolution"
 ATTR_LAMP_HOURS = "lamp_hours"
 ATTR_LAMP_HOURS = "lamp_hours"
+ATTR_AV_MUTE = "av_mute"
+ATTR_FREEZE = "freeze"
+
 
 
 class ProjectorState(StrEnum):
 class ProjectorState(StrEnum):
     OFF = "off"
     OFF = "off"
     ON = "on"
     ON = "on"
     COOLING = "cooling"
     COOLING = "cooling"
-    WARMING = "warming"
+    WARMING = "warming"
+

+ 39 - 0
custom_components/pjlink2/media_player.py

@@ -34,6 +34,7 @@ from homeassistant.const import (
 from homeassistant.core import HomeAssistant as HomeAssistantType
 from homeassistant.core import HomeAssistant as HomeAssistantType
 
 
 import homeassistant.helpers.config_validation as cv
 import homeassistant.helpers.config_validation as cv
+from homeassistant.helpers import entity_platform
 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
 
 
 import voluptuous as vol
 import voluptuous as vol
@@ -51,6 +52,8 @@ from .const import (
     ATTR_RESOLUTION_X,
     ATTR_RESOLUTION_X,
     ATTR_RESOLUTION_Y,
     ATTR_RESOLUTION_Y,
     ATTR_LAMP_HOURS,
     ATTR_LAMP_HOURS,
+    ATTR_AV_MUTE,
+    ATTR_FREEZE,
     ProjectorState,
     ProjectorState,
 )
 )
 
 
@@ -86,6 +89,15 @@ async def async_setup_platform(
     devices = [PJLink2MediaPlayer(pjl, name, sources)]
     devices = [PJLink2MediaPlayer(pjl, name, sources)]
     async_add_entities(devices, update_before_add=False)
     async_add_entities(devices, update_before_add=False)
 
 
+    platform = entity_platform.async_get_current_platform()
+    platform.async_register_entity_service(
+        "freeze",
+        {
+            vol.Required("freeze"): cv.boolean,
+        },
+        "async_freeze",
+    )
+
 
 
 class PJLink2MediaPlayer(MediaPlayerEntity):
 class PJLink2MediaPlayer(MediaPlayerEntity):
 
 
@@ -93,6 +105,7 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
         MediaPlayerEntityFeature.TURN_ON
         MediaPlayerEntityFeature.TURN_ON
         | MediaPlayerEntityFeature.TURN_OFF
         | MediaPlayerEntityFeature.TURN_OFF
         | MediaPlayerEntityFeature.SELECT_SOURCE
         | MediaPlayerEntityFeature.SELECT_SOURCE
+        | MediaPlayerEntityFeature.MUTE_VOLUME
     )
     )
 
 
     def __init__(self, pjl, name, sources):
     def __init__(self, pjl, name, sources):
@@ -158,6 +171,18 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
     def extra_state_attributes(self) -> dict[str, Any]:
     def extra_state_attributes(self) -> dict[str, Any]:
         return self.attrs
         return self.attrs
 
 
+    @property
+    def is_volume_muted(self) -> bool | None:
+        return self.attrs.get(ATTR_AV_MUTE)
+
+    async def async_mute_volume(self, mute: bool) -> None:
+        await self._projector.mute.both(mute)
+        self.attrs[ATTR_AV_MUTE] = mute
+
+    async def async_freeze(self, freeze: bool) -> None:
+        await self._projector.freeze.set(freeze)
+        self.attrs[ATTR_FREEZE] = freeze
+
     async def async_turn_on(self) -> None:
     async def async_turn_on(self) -> None:
         await Power(self._projector).set(Power.ON)
         await Power(self._projector).set(Power.ON)
         self._state = MediaPlayerState.ON
         self._state = MediaPlayerState.ON
@@ -240,9 +265,23 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
                     self.attrs.pop(ATTR_RESOLUTION_X, None)
                     self.attrs.pop(ATTR_RESOLUTION_X, None)
                     self.attrs.pop(ATTR_RESOLUTION_Y, None)
                     self.attrs.pop(ATTR_RESOLUTION_Y, None)
 
 
+                try:
+                    mute_status = await self._projector.mute.status()
+                    # status() returns (video_muted, audio_muted)
+                    self.attrs[ATTR_AV_MUTE] = mute_status[0] or mute_status[1]
+                except Exception:
+                    pass
+
+                try:
+                    self.attrs[ATTR_FREEZE] = await self._projector.freeze.get()
+                except Exception:
+                    pass
+
             elif pwr == Power.State.OFF:
             elif pwr == Power.State.OFF:
                 self.attrs.pop(ATTR_RESOLUTION_X, None)
                 self.attrs.pop(ATTR_RESOLUTION_X, None)
                 self.attrs.pop(ATTR_RESOLUTION_Y, None)
                 self.attrs.pop(ATTR_RESOLUTION_Y, None)
+                self.attrs.pop(ATTR_AV_MUTE, None)
+                self.attrs.pop(ATTR_FREEZE, None)
                 self._current_source = None
                 self._current_source = None
 
 
             self._connectionErrorLogged = False
             self._connectionErrorLogged = False

+ 14 - 0
custom_components/pjlink2/services.yaml

@@ -0,0 +1,14 @@
+freeze:
+  name: Freeze
+  description: Freeze the current image on the projector.
+  target:
+    entity:
+      integration: pjlink2
+      domain: media_player
+  fields:
+    freeze:
+      name: Freeze
+      description: True to freeze, False to unfreeze.
+      required: true
+      selector:
+        boolean: