Skip to content

Commit

Permalink
Merge pull request #4 from NitorCreations/more-work
Browse files Browse the repository at this point in the history
Add device information, media player entities, some refactoring
  • Loading branch information
Jalle19 authored Sep 10, 2024
2 parents f17d669 + 6d161e4 commit 643a835
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 26 deletions.
56 changes: 49 additions & 7 deletions custom_components/vinx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,69 @@
from __future__ import annotations
from dataclasses import dataclass

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.device_registry import DeviceInfo, format_mac

from custom_components.vinx.const import DOMAIN
from custom_components.vinx.lw3 import LW3

PLATFORMS: list[Platform] = [Platform.MEDIA_PLAYER]


@dataclass
class DeviceInformation:
mac_address: str
product_name: str
device_info: DeviceInfo


@dataclass
class VinxRuntimeData:
lw3: LW3
device_information: DeviceInformation


async def get_device_information(lw3: LW3) -> DeviceInformation:
async with lw3.connection():
mac_address = str(await lw3.get_property("/.MacAddress"))
product_name = str(await lw3.get_property("/.ProductName"))
device_label = str(await lw3.get_property("/SYS/MB.DeviceLabel"))
firmware_version = str(await lw3.get_property("/.FirmwareVersion"))
serial_number = str(await lw3.get_property("/.SerialNumber"))
ip_address = str(await lw3.get_property("/MANAGEMENT/NETWORK.IpAddress"))

device_info = DeviceInfo(
identifiers={(DOMAIN, format_mac(mac_address))},
name=f"{device_label} ({product_name})",
manufacturer="Lightware",
model=product_name,
sw_version=firmware_version,
serial_number=serial_number,
configuration_url=f"http://{ip_address}/",
)

return DeviceInformation(mac_address, product_name, device_info)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up from a config entry."""
# Verify we can connect
if "host" in entry.data and "port" in entry.data:
lw3 = LW3(entry.data["host"], entry.data["port"])
else:
raise KeyError("Config entry is missing required parameters")

try:
device = LW3(entry.data["host"], entry.data["port"])
await device.connect()
# Store runtime information
async with lw3.connection():
device_information = await get_device_information(lw3)

# Store the lw3 as runtime data in the entry
entry.runtime_data = VinxRuntimeData(lw3, device_information)
except ConnectionError as e:
raise ConfigEntryNotReady("Unable to connect") from e

# Store the device as runtime data in teh entry
entry.runtime_data = device

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True
Expand Down
18 changes: 8 additions & 10 deletions custom_components/vinx/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None) -> Con
errors: dict[str, str] = {}
if user_input is not None:
try:
# Try to connect to the device
lw3_device = LW3(user_input["host"], user_input["port"])
await lw3_device.connect()

# Make a title for the entry
title = str(await lw3_device.get_property("/.ProductName"))

# Disconnect, this was just for validation
await lw3_device.disconnect()
except (ConnectionError, OSError):
# Query the device for enough information to make an entry title
lw3 = LW3(user_input["host"], user_input["port"])
async with lw3.connection():
product_name = await lw3.get_property("/.ProductName")
device_label = await lw3.get_property("/SYS/MB.DeviceLabel")

title = f"{device_label} ({product_name})"
except (BrokenPipeError, ConnectionError, OSError): # all technically OSError
errors["base"] = "cannot_connect"
else:
return self.async_create_entry(title=title, data=user_input)
Expand Down
18 changes: 16 additions & 2 deletions custom_components/vinx/lw3.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ async def _read_until(self, phrase: str) -> str | None:
if b.endswith(phrase.encode()):
return b.decode()

async def connect(self):
def connection(self):
return LW3ConnectionContext(self)

async def _connect(self):
self._reader, self._writer = await asyncio.open_connection(self._hostname, self._port)

async def disconnect(self):
async def _disconnect(self):
self._writer.close()
await self._writer.wait_closed()

Expand Down Expand Up @@ -99,3 +102,14 @@ async def get_property(self, path: str) -> PropertyResponse:

async def set_property(self, path: str, value: str) -> PropertyResponse:
return await asyncio.wait_for(self._run_set_property(path, value), self._timeout)


class LW3ConnectionContext:
def __init__(self, lw3: LW3):
self._lw3 = lw3

async def __aenter__(self):
await self._lw3._connect()

async def __aexit__(self, *args):
await self._lw3._disconnect()
60 changes: 53 additions & 7 deletions custom_components/vinx/media_player.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,67 @@
import logging

from homeassistant.components.media_player import MediaPlayerEntity
from homeassistant.components.media_player import MediaPlayerEntity, MediaPlayerEntityFeature, MediaPlayerState
from homeassistant.helpers.device_registry import DeviceInfo

from custom_components.vinx import LW3
from custom_components.vinx import LW3, DeviceInformation, VinxRuntimeData

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass, entry, async_add_entities):
# Extract stored runtime data
lw3_device: LW3 = entry.runtime_data
_LOGGER.info(lw3_device)
runtime_data: VinxRuntimeData = entry.runtime_data
_LOGGER.info(f"Runtime data: {runtime_data}")

# Add entity to Home Assistant
product_name = runtime_data.device_information.product_name
if product_name.endswith("ENC"):
async_add_entities([VinxEncoder(runtime_data.lw3, runtime_data.device_information)])
pass
elif product_name.endswith("DEC"):
async_add_entities([VinxDecoder(runtime_data.lw3, runtime_data.device_information)])
pass
else:
_LOGGER.warning("Unknown device type, no entities will be added")

class VinxEncoder(MediaPlayerEntity):
pass

class AbstractVinxDevice(MediaPlayerEntity):
def __init__(self, lw3: LW3, device_information: DeviceInformation) -> None:
self._lw3 = lw3
self._device_information = device_information

self._device_class = "receiver"
self._state = MediaPlayerState.IDLE

@property
def device_class(self):
return self._device_class

@property
def unique_id(self) -> str | None:
mac_address = self._device_information.mac_address

return f"vinx_{mac_address}_media_player"

@property
def state(self):
return self._state

class VinxDecoder(MediaPlayerEntity):
@property
def device_info(self) -> DeviceInfo:
return self._device_information.device_info

@property
def name(self):
return "Media Player"


class VinxEncoder(AbstractVinxDevice):
pass


class VinxDecoder(AbstractVinxDevice):
def __init__(self, lw3: LW3, device_information: DeviceInformation) -> None:
super().__init__(lw3, device_information)

_attr_supported_features = MediaPlayerEntityFeature.SELECT_SOURCE

0 comments on commit 643a835

Please sign in to comment.