Skip to content

Commit ffa9e3f

Browse files
authored
feat: manual setup mode (#18)
Allow to enter an IP address during setup. If left empty, auto-discovery is started, otherwise the setup process tries to retrieve device information from the entered address.
1 parent 000d902 commit ffa9e3f

File tree

4 files changed

+68
-21
lines changed

4 files changed

+68
-21
lines changed

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ To do that, we need to compile it on the target architecture as `pyinstaller` do
6969

7070
On x86-64 Linux we need Qemu to emulate the aarch64 target platform:
7171
```bash
72-
sudo apt-get install qemu binfmt-support qemu-user-static
72+
sudo apt install qemu binfmt-support qemu-user-static
7373
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
7474
```
7575

@@ -81,8 +81,7 @@ docker run --rm --name builder \
8181
-v "$PWD":/workspace \
8282
docker.io/unfoldedcircle/r2-pyinstaller:3.11.6 \
8383
bash -c \
84-
"cd /workspace && \
85-
python -m pip install -r requirements.txt && \
84+
"python -m pip install -r requirements.txt && \
8685
pyinstaller --clean --onefile --name intg-androidtv intg-androidtv/driver.py"
8786
```
8887

@@ -95,8 +94,7 @@ docker run --rm --name builder \
9594
-v "$PWD":/workspace \
9695
docker.io/unfoldedcircle/r2-pyinstaller:3.11.6 \
9796
bash -c \
98-
"cd /workspace && \
99-
python -m pip install -r requirements.txt && \
97+
"python -m pip install -r requirements.txt && \
10098
pyinstaller --clean --onefile --name intg-androidtv intg-androidtv/driver.py"
10199
```
102100

driver.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"driver_id": "uc_androidtv_driver",
3-
"version": "0.4.3",
3+
"version": "0.4.4",
44
"min_core_api": "0.20.0",
55
"name": { "en": "Android TV" },
66
"icon": "uc:integration",
@@ -28,7 +28,7 @@
2828
"field": {
2929
"label": {
3030
"value": {
31-
"en": "The integration will discover your Android TV on your network. Only devices running the [Android TV Remote Service](https://play.google.com/store/apps/details?id=com.google.android.tv.remote.service) are supported.\nDuring the process, you might need to enter a PIN that is shown on your Android TV. Please make sure that your Android TV is powered on and that no old paring request is shown.\nIf paring continuously fails, reboot your Android TV device and try again."
31+
"en": "The integration will discover your Android TV on your network. Only devices running the [Android TV Remote Service](https://play.google.com/store/apps/details?id=com.google.android.tv.remote.service) are supported.\nDuring the process, you might need to enter a PIN that is shown on your Android TV. Please make sure that your Android TV is powered on and that no old pairing request is shown.\nIf pairing continuously fails, reboot your Android TV device and try again."
3232
}
3333
}
3434
}

intg-androidtv/setup_flow.py

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ class SetupSteps(IntEnum):
2828
"""Enumeration of setup steps to keep track of user data responses."""
2929

3030
INIT = 0
31-
DEVICE_CHOICE = 1
32-
PAIRING_PIN = 2
31+
CONFIGURATION_MODE = 1
32+
DEVICE_CHOICE = 2
33+
PAIRING_PIN = 3
3334

3435

3536
_setup_step = SetupSteps.INIT
@@ -54,8 +55,10 @@ async def driver_setup_handler(msg: SetupDriver) -> SetupAction:
5455
return await handle_driver_setup(msg)
5556
if isinstance(msg, UserDataResponse):
5657
_LOG.debug("UserDataResponse: %s", msg)
58+
if _setup_step == SetupSteps.CONFIGURATION_MODE and "address" in msg.input_values:
59+
return await handle_configuration_mode(msg)
5760
if _setup_step == SetupSteps.DEVICE_CHOICE and "choice" in msg.input_values:
58-
return await handle_user_data_choice(msg)
61+
return await handle_device_choice(msg)
5962
if _setup_step == SetupSteps.PAIRING_PIN and "pin" in msg.input_values:
6063
return await handle_user_data_pin(msg)
6164
_LOG.error("No or invalid user response was received: %s", msg)
@@ -78,31 +81,76 @@ async def handle_driver_setup(_msg: DriverSetupRequest) -> RequestUserInput | Se
7881
Start driver setup.
7982
8083
Initiated by Remote Two to set up the driver.
81-
Start Android TV discovery and present the found devices to the user to choose from.
84+
Ask user to enter ip-address for manual configuration, otherwise auto-discovery is used.
8285
8386
:param _msg: not used, we don't have any input fields in the first setup screen.
8487
:return: the setup action on how to continue
8588
"""
89+
global _setup_step
90+
91+
_LOG.debug("Starting driver setup")
92+
_setup_step = SetupSteps.CONFIGURATION_MODE
93+
return RequestUserInput(
94+
{"en": "Setup mode", "de": "Setup Modus"},
95+
[
96+
{"field": {"text": {"value": ""}}, "id": "address", "label": {"en": "IP address", "de": "IP-Adresse"}},
97+
{
98+
"id": "info",
99+
"label": {"en": ""},
100+
"field": {
101+
"label": {
102+
"value": {
103+
"en": "Leave blank to use auto-discovery.",
104+
"de": "Leer lassen, um automatische Erkennung zu verwenden.",
105+
"fr": "Laissez le champ vide pour utiliser la découverte automatique.",
106+
}
107+
}
108+
},
109+
},
110+
],
111+
)
112+
113+
114+
async def handle_configuration_mode(msg: UserDataResponse) -> RequestUserInput | SetupError:
115+
"""
116+
Process user data response in a setup process.
117+
118+
If ``address`` field is set by the user: try connecting to device and retrieve model information.
119+
Otherwise, start Android TV discovery and present the found devices to the user to choose from.
120+
121+
:param msg: response data from the requested user data
122+
:return: the setup action on how to continue
123+
"""
86124
global _discovered_android_tvs
87125
global _pairing_android_tv
88126
global _setup_step
89127

90-
_LOG.info("Starting driver setup with Android TV discovery")
91-
92128
# clear all configured devices and any previous pairing attempt
93129
if _pairing_android_tv:
94130
_pairing_android_tv.disconnect()
95131
_pairing_android_tv = None
96132
config.devices.clear() # triggers device instance removal
97133

98-
# start discovery
99-
_discovered_android_tvs = await discover.android_tvs()
100-
101134
dropdown_items = []
102-
for discovered_tv in _discovered_android_tvs:
103-
tv_data = {"id": discovered_tv["address"], "label": {"en": discovered_tv["label"]}}
104-
105-
dropdown_items.append(tv_data)
135+
address = msg.input_values["address"]
136+
137+
if address:
138+
_LOG.debug("Starting manual driver setup for %s", address)
139+
# Connect to device and retrieve name
140+
android_tv = tv.AndroidTv(config.devices.data_path, address, "")
141+
res = await android_tv.init(20)
142+
if res is False:
143+
return SetupError(error_type=IntegrationSetupError.TIMEOUT)
144+
dropdown_items.append({"id": address, "label": {"en": f"{android_tv.name} [{address}]"}})
145+
else:
146+
_LOG.debug("Starting driver setup with Android TV discovery")
147+
# start discovery
148+
_discovered_android_tvs = await discover.android_tvs()
149+
150+
for discovered_tv in _discovered_android_tvs:
151+
tv_data = {"id": discovered_tv["address"], "label": {"en": discovered_tv["label"]}}
152+
153+
dropdown_items.append(tv_data)
106154

107155
if not dropdown_items:
108156
_LOG.warning("No Android TVs found")
@@ -126,7 +174,7 @@ async def handle_driver_setup(_msg: DriverSetupRequest) -> RequestUserInput | Se
126174
)
127175

128176

129-
async def handle_user_data_choice(msg: UserDataResponse) -> RequestUserInput | SetupError:
177+
async def handle_device_choice(msg: UserDataResponse) -> RequestUserInput | SetupError:
130178
"""
131179
Process user data device choice response in a setup process.
132180

intg-androidtv/tv.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ async def _handle_connection_failure(self, connect_duration: float, ex):
318318

319319
def disconnect(self) -> None:
320320
"""Disconnect from Android TV."""
321+
self._reconnect_delay = MIN_RECONNECT_DELAY
321322
self._atv.disconnect()
322323
self.events.emit(Events.DISCONNECTED, self._identifier)
323324

0 commit comments

Comments
 (0)