Преглед на файлове

try reseting the tcp socket + try out hardcoded friendly names

Noah Vogt преди 2 месеца
родител
ревизия
d516a24dc9
променени са 1 файла, в които са добавени 45 реда и са изтрити 30 реда
  1. 45 30
      custom_components/pjlink2/media_player.py

+ 45 - 30
custom_components/pjlink2/media_player.py

@@ -54,7 +54,6 @@ from .const import (
 )
 
 _LOGGER = logging.getLogger(__name__)
-# Time between updating data from projector
 SCAN_INTERVAL = timedelta(seconds=3)
 
 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
@@ -105,9 +104,16 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
         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"]
+        # --- FRIENDLY NAMES MAPPING ---
+        # Change these values to whatever you want them to be called in Home Assistant
+        self._source_mapping = {
+            "31": "HDMI 1",
+            "32": "HDMI 2",
+            "33": "HDMI 3",
+            "11": "Computer 1",
+        }
+        self._reverse_mapping = {v: k for k, v in self._source_mapping.items()}
+        self._source_list = list(self._source_mapping.values())
 
     async def async_will_remove_from_hass(self) -> None:
         """Close connection."""
@@ -115,10 +121,8 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
         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)
-                )
+            except Exception:
+                pass
 
     @property
     def name(self) -> str:
@@ -138,33 +142,30 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
 
     @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]
+        # Convert friendly name (e.g. "HDMI 1") back to raw PJLink code (e.g. "31")
+        raw_source = self._reverse_mapping.get(source, source)
+        source_type = raw_source[0]
+        source_index = raw_source[1]
 
         await Sources(self._projector).set(source_type, source_index)
         self._current_source = source
@@ -197,18 +198,27 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
                 try:
                     current = await Sources(self._projector).get()
                     if isinstance(current, (tuple, list)):
-                        self._current_source = "".join(map(str, current))
+                        raw_source = "".join(map(str, current))
                     else:
-                        self._current_source = str(current)
-                except PJLinkException:
-                    pass  # Keep previous state if temporarily unavailable
+                        raw_source = str(current)
+
+                    # Apply friendly name mapping
+                    self._current_source = self._source_mapping.get(
+                        raw_source, raw_source
+                    )
+
+                except Exception as e:
+                    # THE FIX: If the projector is busy (ERR3), bubble the error up so we force a disconnect
+                    if "ERR3" in repr(e) or "unavailable" in repr(e):
+                        raise e
+                    _LOGGER.debug("Ignored error getting source: %s", repr(e))
 
                 # 2. Fetch Lamp Hours
                 try:
                     self.attrs[ATTR_LAMP_HOURS] = await Lamp(
                         self._projector
                     ).hours()
-                except PJLinkException:
+                except Exception:
                     pass
 
                 # 3. Fetch Resolution
@@ -216,7 +226,7 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
                     res = await Sources(self._projector).resolution()
                     self.attrs[ATTR_RESOLUTION_X] = res[0]
                     self.attrs[ATTR_RESOLUTION_Y] = res[1]
-                except PJLinkException:
+                except Exception:
                     self.attrs.pop(ATTR_RESOLUTION_X, None)
                     self.attrs.pop(ATTR_RESOLUTION_Y, None)
 
@@ -227,11 +237,22 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
 
             self._connectionErrorLogged = False
 
-        except (PJLinkException, OSError) as err:
+        except Exception as err:
             err_str = repr(err)
-            # If the projector is just busy switching inputs/states, ignore and wait for next poll
+
+            # THE FIX: Forcefully drop the connection on any error.
+            # This prevents the projector from getting "stuck" on a stale socket.
+            if self._available:
+                self._available = False
+                try:
+                    await self._projector.__aexit__(0, 0, 0)
+                except Exception:
+                    pass
+
             if "ERR3" in err_str or "unavailable" in err_str:
-                pass
+                _LOGGER.debug(
+                    "Projector is busy switching inputs. Reconnecting next poll."
+                )
             else:
                 if not self._connectionErrorLogged:
                     _LOGGER.error(
@@ -239,9 +260,3 @@ class PJLink2MediaPlayer(MediaPlayerEntity):
                     )
                     self._connectionErrorLogged = True
                 self._state = MediaPlayerState.OFF
-                if self._available:
-                    self._available = False
-                    try:
-                        await self._projector.__aexit__(0, 0, 0)
-                    except Exception:
-                        pass