From ee79c84c6941d3db2606662283ab0eeba1586c8d Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Fri, 6 Sep 2024 14:59:37 +0300 Subject: [PATCH 1/2] Remove unused getters from LW3 --- custom_components/vinx/lw3.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/custom_components/vinx/lw3.py b/custom_components/vinx/lw3.py index 6f4ae45..e09946d 100644 --- a/custom_components/vinx/lw3.py +++ b/custom_components/vinx/lw3.py @@ -95,12 +95,3 @@ 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) - - async def get_product_name(self): - return str(await self._run_get_property("/.ProductName")) - - async def get_serial_number(self): - return str(await self._run_get_property("/.SerialNumber")) - - async def get_mac_address(self): - return str(await self._run_get_property("/.MacAddress")) From 1fd85321268010419f2a0e44b74fee38d24f9353 Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Fri, 6 Sep 2024 14:59:53 +0300 Subject: [PATCH 2/2] Add initial Home Assistant integration boilerplate --- custom_components/vinx/__init__.py | 32 +++++++++++++++++ custom_components/vinx/config_flow.py | 40 +++++++++++++++++++++ custom_components/vinx/const.py | 6 ++++ custom_components/vinx/lw3.py | 4 +++ custom_components/vinx/manifest.json | 17 +++++++++ custom_components/vinx/media_player.py | 21 +++++++++++ custom_components/vinx/strings.json | 19 ++++++++++ custom_components/vinx/translations/en.json | 19 ++++++++++ 8 files changed, 158 insertions(+) create mode 100644 custom_components/vinx/__init__.py create mode 100644 custom_components/vinx/config_flow.py create mode 100644 custom_components/vinx/const.py create mode 100644 custom_components/vinx/manifest.json create mode 100644 custom_components/vinx/media_player.py create mode 100644 custom_components/vinx/strings.json create mode 100644 custom_components/vinx/translations/en.json diff --git a/custom_components/vinx/__init__.py b/custom_components/vinx/__init__.py new file mode 100644 index 0000000..e7daa56 --- /dev/null +++ b/custom_components/vinx/__init__.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady + +from custom_components.vinx.lw3 import LW3 + +PLATFORMS: list[Platform] = [Platform.MEDIA_PLAYER] + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up from a config entry.""" + # Verify we can connect + try: + device = LW3(entry.data["host"], entry.data["port"]) + await device.connect() + 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 + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/custom_components/vinx/config_flow.py b/custom_components/vinx/config_flow.py new file mode 100644 index 0000000..cd4b95d --- /dev/null +++ b/custom_components/vinx/config_flow.py @@ -0,0 +1,40 @@ +from typing import Any + +import voluptuous as vol + +from homeassistant.config_entries import ConfigFlow, ConfigFlowResult + +from .const import CONF_HOST, CONF_PORT, DOMAIN +from .lw3 import LW3 + +STEP_USER_DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_HOST): str, + vol.Required(CONF_PORT, default=6107): int, + } +) + + +class VinxConfigFlow(ConfigFlow, domain=DOMAIN): + VERSION = 1 + + async def async_step_user(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult: + """Handle user initiated configuration""" + 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): + errors["base"] = "cannot_connect" + else: + return self.async_create_entry(title=title, data=user_input) + + return self.async_show_form(step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors) diff --git a/custom_components/vinx/const.py b/custom_components/vinx/const.py new file mode 100644 index 0000000..6372df7 --- /dev/null +++ b/custom_components/vinx/const.py @@ -0,0 +1,6 @@ +DOMAIN = "vinx" + +CONF_HOST = "host" +CONF_PORT = "port" +CONF_PASSWORD = "password" +CONF_DEVICE_TYPE = "device_type" diff --git a/custom_components/vinx/lw3.py b/custom_components/vinx/lw3.py index e09946d..ee228be 100644 --- a/custom_components/vinx/lw3.py +++ b/custom_components/vinx/lw3.py @@ -50,6 +50,10 @@ async def _read_until(self, phrase: str) -> str | None: async def connect(self): self._reader, self._writer = await asyncio.open_connection(self._hostname, self._port) + async def disconnect(self): + self._writer.close() + await self._writer.wait_closed() + @staticmethod def _is_error_response(response: str) -> bool: return response[1] == "E" diff --git a/custom_components/vinx/manifest.json b/custom_components/vinx/manifest.json new file mode 100644 index 0000000..026f0dd --- /dev/null +++ b/custom_components/vinx/manifest.json @@ -0,0 +1,17 @@ +{ + "domain": "vinx", + "name": "VINX", + "version": "0.0.1", + "codeowners": [ + "@Jalle19" + ], + "config_flow": true, + "integration_type": "device", + "dependencies": [], + "documentation": "https://www.home-assistant.io/integrations/vinx", + "homekit": {}, + "iot_class": "local_polling", + "requirements": [], + "ssdp": [], + "zeroconf": [] +} diff --git a/custom_components/vinx/media_player.py b/custom_components/vinx/media_player.py new file mode 100644 index 0000000..3ef3d28 --- /dev/null +++ b/custom_components/vinx/media_player.py @@ -0,0 +1,21 @@ +import logging + +from homeassistant.components.media_player import MediaPlayerEntity + +from custom_components.vinx import LW3 + +_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) + + +class VinxEncoder(MediaPlayerEntity): + pass + + +class VinxDecoder(MediaPlayerEntity): + pass diff --git a/custom_components/vinx/strings.json b/custom_components/vinx/strings.json new file mode 100644 index 0000000..4904807 --- /dev/null +++ b/custom_components/vinx/strings.json @@ -0,0 +1,19 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "[%key:common::config_flow::data::host%]", + "port": "[%key:common::config_flow::data::port%]" + } + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + } +} diff --git a/custom_components/vinx/translations/en.json b/custom_components/vinx/translations/en.json new file mode 100644 index 0000000..5492c4d --- /dev/null +++ b/custom_components/vinx/translations/en.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "unknown": "Unexpected error" + }, + "step": { + "user": { + "data": { + "host": "Host", + "port": "Port" + } + } + } + } +} \ No newline at end of file