Skip to content

Commit e9b6744

Browse files
committed
Correct device setup guidance
1 parent e6c6ff3 commit e9b6744

22 files changed

Lines changed: 470 additions & 60 deletions

File tree

‎README.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The launcher guides the deployment from start to finish:
2525
1. Choose Docker on the current computer, a home NAS/Docker-capable Arduino Linux board, or a remote Linux server.
2626
2. Choose whether the Web UI is available only on this computer or also to other devices on a trusted home LAN.
2727
3. Select the Web UI port. If it is occupied, the launcher suggests a nearby free port.
28-
4. Enter the inverter hostname/IP and TCP port, normally `8000`.
28+
4. Enter the inverter hostname/IP and start with TCP port `8000`; the logger or firmware may use a different port.
2929
5. Build the image and optionally test the inverter connection from the same container network used by InverterScout.
3030
6. Start the service, wait for its healthcheck, and open the first-run browser wizard.
3131

@@ -75,9 +75,9 @@ After deployment, complete these browser steps:
7575
2. Enter the inverter address, TCP port, Wi-Fi dongle serial, and inverter serial.
7676
3. Choose a polling interval.
7777
4. Configure a private Telegram bot or explicitly choose operation without Telegram.
78-
5. Add Tapo or Tuya credentials and devices from Settings and Devices.
78+
5. Follow the [device connection guide](docs/DEVICE_SETUP.md), then add Tapo or Tuya credentials in Settings and devices in Devices.
7979

80-
See the [deployment guide](docs/DEPLOYMENT.md) for prerequisites, troubleshooting, update paths, and service-management commands.
80+
See the [deployment guide](docs/DEPLOYMENT.md) for prerequisites, troubleshooting, update paths, and service-management commands. The device guide explains the supported LuxPower dongle flow, Tapo account requirements, and the mandatory Tuya Developer Cloud project.
8181

8282
## Interface
8383

@@ -137,8 +137,8 @@ Do not rely on an experimental model for automation or critical monitoring. Ever
137137

138138
### Smart devices and deployment hosts
139139

140-
- Tapo switches and bulbs supported by the bundled Tapo library
141-
- Tuya LAN switches and bulbs supported by the bundled Tuya library
140+
- Tapo switches and bulbs supported by the bundled, unofficial Tapo local API library; pair them in the official Tapo app first and check the library's [current package documentation](https://pypi.org/project/tapo/)
141+
- Tuya LAN switches and bulbs using a Local Key obtained from an authorized [Tuya Developer Cloud](https://platform.tuya.com/cloud/) Smart Home project; consumer-app credentials alone are not sufficient
142142
- Docker hosts on x86-64 or ARM64 Linux, a home computer, or a NAS
143143
- Docker-capable Arduino Linux boards such as Portenta X8 are an experimental target
144144

@@ -185,7 +185,7 @@ Run the Quick Start launcher in a dedicated directory. On macOS or Windows, keep
185185

186186
### NAS
187187

188-
Create a project in the NAS container manager from `docker-compose.yml`, keep the named volume mounted at `/app/data`, and publish port `8080` only on the NAS LAN address. The container needs outbound internet access only when Telegram or Tuya Cloud discovery is enabled, plus local TCP access to the inverter and smart devices.
188+
Create a project in the NAS container manager from `docker-compose.yml`, keep the named volume mounted at `/app/data`, and publish port `8080` only on the NAS LAN address. The container needs outbound internet access only when Telegram is enabled or Tuya device metadata and Local Keys are retrieved, plus local TCP access to the inverter and smart devices. Existing Tuya devices are controlled over the LAN after their Local Keys have been stored.
189189

190190
### ARM64 single-board computer
191191

‎docs/DEPLOYMENT.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,12 @@ The uploaded archive contains only the runtime build files selected by the launc
7878

7979
The deployment launcher asks for the inverter host and port only to test network reachability. It deliberately does not save them in `.env`. Enter them again in the browser wizard so the application can store them in its encrypted database.
8080

81+
Complete the LuxPower Wi-Fi dongle setup before the browser wizard and use its LAN address from the router's client or DHCP list. Port `8000` is the default verified by InverterScout, not a manufacturer guarantee for every logger or firmware version. Do not expose the dongle port through router forwarding.
82+
8183
If Telegram is enabled, create a private bot with `@BotFather`, enter the token, and provide the administrator chat ID. The wizard does not send a test message. Every additional Telegram user remains pending until manually approved.
8284

85+
Tapo and Tuya are configured after first run. Tuya requires a Smart Home project and authorized app-account link on Tuya Developer Cloud; a Tuya Smart or Smart Life login cannot be entered directly. Follow the [device connection guide](DEVICE_SETUP.md) before adding either provider.
86+
8387
## Common failures
8488

8589
### Docker command not found
@@ -121,6 +125,14 @@ Confirm the inverter address and TCP port, then check:
121125

122126
Skipping the test starts the Web UI but does not fix the network route.
123127

128+
### Tuya device is not found
129+
130+
Confirm that the Tuya Developer Cloud project uses the **Smart Home** development method, its required cloud services are active, and the correct Tuya Smart or Smart Life account is linked under **Devices > Link App Account**. Select the same data-center region in InverterScout that the cloud project uses. Resetting or re-pairing a device changes its Local Key, so remove and add that device again.
131+
132+
### Smart device is offline
133+
134+
Reserve the device's IPv4 address in the router and confirm the Docker host can reach it on the same trusted LAN. For Tuya, verify the selected local protocol version with a TinyTuya network scan. For Tapo, confirm the TP-Link ID can control the device in the official Tapo app and that the model is supported by the current Tapo library.
135+
124136
### Remote Docker permission denied
125137

126138
The SSH account cannot access the Docker daemon. Configure Docker access according to the remote operating system's security policy. The launcher does not send a password to `sudo` or modify group membership.

‎docs/DEVICE_SETUP.md‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Device Connection Guide
2+
3+
InverterScout reads the LuxPower inverter through its Wi-Fi dongle and controls supported Tapo or Tuya devices on the trusted local network. Complete each manufacturer's normal pairing flow before entering anything in InverterScout.
4+
5+
Never forward the inverter, smart-device, or Web UI ports through the internet router. Reserve local addresses in the router where possible so a DHCP renewal does not break monitoring.
6+
7+
## LuxPower Wi-Fi dongle
8+
9+
1. Install the dongle on the inverter and complete its network setup in the LuxPower app. LuxPowerTek describes the dongle as an RS485 communication module that joins the network through its app and web platform; see the [official Wi-Fi dongle page](https://luxpowertek.com/product-data-logger-wifi-dongle/) and [official SNA 3–6K manual](https://luxpowertek.com/wp-content/uploads/2025/12/SNA-3-6K-User-manual-English-OffGrid-Single-Phase-LuxpowerTek.pdf).
10+
2. Open the home router's connected-client or DHCP list, identify the dongle, and reserve its IPv4 address. Enter that address as the inverter host. Do not enter the Docker host's address.
11+
3. Start with TCP port `8000`. This is the default verified by InverterScout for the tested logger, but LuxPowerTek does not document it as a universal setting for every dongle and firmware version.
12+
4. Enter the exact 10-character Wi-Fi dongle and inverter serial numbers printed on their labels. Do not paste either serial into an issue, screenshot, or public diagnostic archive.
13+
5. Keep the Docker host and dongle on the same trusted LAN, an allowed VLAN route, or a private VPN. The connection is outbound from InverterScout; no inverter port mapping belongs in Docker Compose.
14+
15+
The inverter integration sends only Modbus function `0x04` read-input-register requests. A model listed as expected or experimental in the README still requires independent hardware validation.
16+
17+
## Tapo switches and bulbs
18+
19+
1. Pair the device in the official Tapo app and verify that it can be controlled there.
20+
2. Reserve the device's IPv4 address in the router.
21+
3. Open **Settings > Tapo** in InverterScout. Enter the email address and password of the TP-Link ID that can control the device. InverterScout never renders the saved values back into the page.
22+
4. Open **Devices > Add device**, choose **Tapo**, enter the reserved local address and a display name, then add the device.
23+
24+
The bundled [Tapo package](https://pypi.org/project/tapo/) is an unofficial local API client. Compatibility depends on the exact model and firmware even when the official Tapo app works. InverterScout currently provides on/off control through the generic device API and power readings for P110 and P115 plugs.
25+
26+
For better account separation, a dedicated TP-Link ID can be created and the official Tapo app's [Device Sharing](https://www.tp-link.com/us/support/faq/5121/) feature can grant it access. Shared-account support varies by model, firmware, and permission tier and is not guaranteed by the local API library. Confirm control with the dedicated account before saving it in InverterScout.
27+
28+
## Tuya switches and bulbs
29+
30+
Tuya Smart or Smart Life credentials cannot be entered directly into InverterScout. Tuya setup requires developer access and an authorized cloud project so InverterScout can retrieve the Device ID metadata and secret Local Key.
31+
32+
### 1. Pair the devices
33+
34+
Pair every device in the **Tuya Smart** or **Smart Life** app using a normal account, not a guest account. Confirm that each device works in the app.
35+
36+
### 2. Request and configure Tuya Developer Cloud access
37+
38+
Follow Tuya's current [Request Tuya Cloud API Key](https://developer.tuya.com/en/docs/developer/apply-cloud-api-key?id=Kff30z8sv62ah) guide:
39+
40+
1. Sign in to [Tuya Developer Cloud](https://platform.tuya.com/cloud/).
41+
2. Open **Cloud > Cloud Project > Project Management**, activate or request the required IoT Core plan, and create a cloud project.
42+
3. Set **Development Method** to **Smart Home**. A different project type will not provide the expected app-account device list.
43+
4. Confirm the required cloud services are subscribed and authorized: **Country and City Info**, **Smart Home Basic Service**, **IoT Core**, and the currently listed Smart Home scene service.
44+
5. Open **Devices > Link App Account > Add App Account**, choose **Tuya App Account Authorization**, and scan the QR code with the same Tuya Smart or Smart Life account used for pairing.
45+
6. Confirm that the devices appear under **All Devices**. Record each required **Device ID**.
46+
7. Copy **Client ID** and **Client Secret** from the project's **Authorization Key** section. InverterScout labels these fields **Access ID** and **Access secret**.
47+
48+
Tuya can change the portal and subscription rules. Treat the linked Tuya guide as authoritative if a label or required service changes. IoT Core access can expire and may require a renewal request.
49+
50+
### 3. Configure InverterScout
51+
52+
1. Open **Settings > Tuya** and enter the project's Client ID, Client Secret, and exact data-center region. Choose the region used by the cloud project, not the region that merely looks geographically closest. Supported codes are `eu`, `eu-w`, `us`, `us-e`, `cn`, `sg`, and `in`.
53+
2. Reserve the Tuya device's IPv4 address in the router.
54+
3. Determine the device's actual local protocol version. The [TinyTuya package documentation](https://pypi.org/project/tinytuya/) supports versions 3.1, 3.2, 3.3, 3.4, and 3.5 and documents `python -m tinytuya scan` for detecting the address, Device ID, and version from a computer on the same LAN.
55+
4. Open **Devices > Add device**, choose **Tuya**, then enter the reserved address, Device ID, and detected protocol version.
56+
57+
When the device is added, InverterScout contacts Tuya Cloud to find that Device ID and retrieve its Local Key. Normal on/off commands then go directly to the device over the LAN. Resetting or re-pairing a Tuya device changes its Local Key; remove and add that device again afterward. The Tuya app can also occupy a device's local connection temporarily, so close it while diagnosing LAN-control failures.
58+
59+
## Credential storage
60+
61+
TP-Link passwords, Tuya Client Secrets, Tuya Local Keys, device settings, and other runtime state are encrypted before being written to the local SQLite database. They are not part of the source tree, container image, screenshots, or sample configuration.
62+
63+
The generated database key is stored separately as `/app/data/.master.key`. Anyone who obtains both the database and that key can decrypt the saved values. Protect Docker volume backups, or inject `INVERTERSCOUT_MASTER_KEY` from a platform secret manager and keep that key backup separate from the database.

‎src/inverterscout/devices/tapo.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""TapoDriver is a driver for controlling TP-Link Tapo devices over LAN.
22
3-
Uses the tapo library (mihai-dinculescu) for local control.
3+
Uses the Tapo library for local control.
44
Supports Tapo P100/P110/P115, L530 and other devices via generic_device().
55
Works with TPAP (lv=2) encryption of new firmware."""
66

‎src/inverterscout/devices/tuya.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
# Device connection timeout in seconds.
1616
CONNECT_TIMEOUT = 5
1717

18+
# Region codes and LAN protocol versions supported by TinyTuya 1.20.
19+
SUPPORTED_TUYA_CLOUD_REGIONS = ("eu", "eu-w", "us", "us-e", "cn", "sg", "in")
20+
SUPPORTED_TUYA_PROTOCOL_VERSIONS = ("3.1", "3.2", "3.3", "3.4", "3.5")
21+
1822

1923
class TuyaDriver(DeviceDriver):
2024
"""Driver for Tuya devices (sockets, switches) via LAN."""

‎src/inverterscout/interfaces/web.py‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
from inverterscout.core.state import Event, StateManager
2323
from inverterscout.devices.manager import DeviceManager
24+
from inverterscout.devices.tuya import (
25+
SUPPORTED_TUYA_CLOUD_REGIONS,
26+
SUPPORTED_TUYA_PROTOCOL_VERSIONS,
27+
)
2428
from inverterscout.settings.i18n import (
2529
SUPPORTED_LANGUAGES,
2630
current_language,
@@ -769,6 +773,7 @@ async def web_devices(request: web.Request) -> dict:
769773
"nav_links": NAV_LINKS,
770774
"msg": msg,
771775
"devices": devices_list,
776+
"tuya_protocol_versions": SUPPORTED_TUYA_PROTOCOL_VERSIONS,
772777
}
773778

774779
async def web_devices_states(request: web.Request) -> web.Response:
@@ -1133,6 +1138,7 @@ async def web_devices_add(request: web.Request) -> web.Response:
11331138
provider = (data.get("provider", "") or "tuya").strip()
11341139
ip = (data.get("ip", "") or "").strip()
11351140
tuya_device_id = (data.get("tuya_device_id", "") or "").strip()
1141+
tuya_protocol_version = (data.get("tuya_protocol_version", "3.3") or "3.3").strip()
11361142
tapo_name = (data.get("tapo_name", "") or "").strip()
11371143
scenario_type = data.get("scenario_type", "none")
11381144
timer_hours = 5
@@ -1152,6 +1158,11 @@ async def web_devices_add(request: web.Request) -> web.Response:
11521158
{"ok": False, "error": translate("web.device_manager_unavailable")}
11531159
)
11541160

1161+
if provider == "tuya" and tuya_protocol_version not in SUPPORTED_TUYA_PROTOCOL_VERSIONS:
1162+
return web.json_response(
1163+
{"ok": False, "error": translate("web.invalid_tuya_protocol_version")}
1164+
)
1165+
11551166
if request.app.get("showcase_mode") and hasattr(_device_mgr, "add_showcase_device"):
11561167
name = tapo_name or translate("showcase.default_tuya_name")
11571168
result = _device_mgr.add_showcase_device(
@@ -1245,8 +1256,12 @@ def _fetch_device():
12451256
{"ok": False, "error": translate("web.tuya_device_not_found")}
12461257
)
12471258

1248-
name = cloud_dev.get("name", tuya_device_id)
1259+
name = cloud_dev.get("name") or tuya_device_id
12491260
local_key = cloud_dev.get("key", "")
1261+
if not local_key:
1262+
return web.json_response(
1263+
{"ok": False, "error": translate("web.tuya_local_key_missing")}
1264+
)
12501265
device_id = "dev_" + tuya_device_id[:8]
12511266

12521267
if device_id in _device_mgr.devices:
@@ -1262,7 +1277,7 @@ def _fetch_device():
12621277
config={
12631278
"device_id": tuya_device_id,
12641279
"local_key": local_key,
1265-
"version": 3.5,
1280+
"version": float(tuya_protocol_version),
12661281
},
12671282
)
12681283

@@ -1518,6 +1533,7 @@ async def web_settings(request: web.Request) -> dict:
15181533
public_settings = {
15191534
"language": stored_settings.get("language", "en"),
15201535
"timezone": stored_settings.get("timezone", "UTC"),
1536+
"tuya_region": stored_settings.get("tuya_region", "eu"),
15211537
}
15221538
return {
15231539
"title": translate("settings.title"),
@@ -1527,6 +1543,7 @@ async def web_settings(request: web.Request) -> dict:
15271543
"settings": public_settings,
15281544
"languages": SUPPORTED_LANGUAGES,
15291545
"timezones": IANA_TIMEZONES,
1546+
"tuya_regions": SUPPORTED_TUYA_CLOUD_REGIONS,
15301547
"credential_status": {
15311548
"tapo": (
15321549
translate("settings.configured")
@@ -1569,7 +1586,7 @@ async def web_settings_action(request: web.Request) -> web.Response:
15691586
access_id = str(data.get("tuya_access_id", "")).strip()
15701587
access_secret = str(data.get("tuya_access_secret", ""))
15711588
region = str(data.get("tuya_region", "eu"))
1572-
if not access_id or not access_secret or region not in {"eu", "us", "cn", "in"}:
1589+
if not access_id or not access_secret or region not in SUPPORTED_TUYA_CLOUD_REGIONS:
15731590
raise web.HTTPBadRequest(text=translate("web.settings_tuya_required"))
15741591
settings["tuya_access_id"] = access_id
15751592
settings["tuya_access_secret"] = access_secret

0 commit comments

Comments
 (0)