Skip to content

Commit 3501a51

Browse files
committed
Initial release v1.0.0
Home Assistant integration for xTool laser cutters and engravers. Features: - Multi-model support (S1, D1, D1 Pro, F1, P2, M1, and more) - Three protocol families: WebSocket M-code, HTTP M-code, REST API - UDP network discovery for automatic device detection - Status monitoring, fill light control, flame alarm, buzzer, exhaust fan - Job control (pause, resume, cancel, home axes) - Lifetime statistics (working time, sessions, standby, tool runtime) - Laser module and accessory detection - Camera support for P2/P2S models - Optional power switch linking (smart plug) - English and German translations
0 parents  commit 3501a51

27 files changed

Lines changed: 3689 additions & 0 deletions

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# APK and decompiled assets (reverse engineering artifacts)
2+
*.apk
3+
decompiled/
4+
extracted_assets/
5+
6+
# Python
7+
__pycache__/
8+
*.py[cod]
9+
*.egg-info/
10+
dist/
11+
build/
12+
13+
# IDE
14+
.vscode/
15+
.idea/
16+
17+
# OS
18+
.DS_Store
19+
Thumbs.db

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 thecodingdad
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# xTool Laser
2+
3+
Home Assistant integration for xTool laser cutters and engravers (S1, D1, D1 Pro, F1, P2, M1, and others).
4+
5+
This integration communicates directly with your xTool device over the local network using the reverse-engineered WebSocket and REST protocols. No cloud connection required.
6+
7+
[![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg)](https://github.com/hacs/integration)
8+
[![GitHub Release](https://img.shields.io/github/v/release/thecodingdad/ha-xtool)](https://github.com/thecodingdad/ha-xtool/releases)
9+
10+
## Features
11+
12+
- Full local control — no cloud, no xTool account required
13+
- Monitor device status (idle, processing, paused, finished, error, ...)
14+
- Control light brightness
15+
- Toggle flame alarm, buzzer, move stop, exhaust fan
16+
- Configure air assist delay, exhaust fan duration
17+
- Flame alarm sensitivity selection (high / low)
18+
- Job control buttons (pause, resume, cancel, home axes)
19+
- Laser position tracking (X/Y/Z)
20+
- Lifetime statistics (working time, session count, standby time, tool runtime)
21+
- Laser module detection (type, power, producer)
22+
- Attached accessories detection (air pump, fire extinguisher, riser base)
23+
- Camera support for P2/P2S models (overview + close-up)
24+
- Optional power switch linking (smart plug control)
25+
- Push state updates via WebSocket (S1) — minimal polling delay
26+
- Automatic reconnect on network interruption
27+
- Multi-model support with auto-detected protocol
28+
29+
## Prerequisites
30+
31+
- Home Assistant 2025.1.0 or newer
32+
- xTool device on the same local network, connected via WiFi
33+
34+
## Supported Devices
35+
36+
The integration supports three protocol families that cover all current xTool models:
37+
38+
| Protocol | Models | Communication |
39+
|----------|--------|---------------|
40+
| WebSocket M-code | S1 | WebSocket (port 8081) + HTTP (port 8080) |
41+
| HTTP M-code | D1, D1 Pro, D1 Pro 2.0 | HTTP POST (port 8080) |
42+
| REST API | F1, F1 Ultra, F1 Lite, M1, M1 Ultra, P1, P2, P2S | HTTP REST (port 8080) |
43+
44+
> **Note:** The integration was developed and tested with an xTool S1. Other models are supported based on reverse engineering of the xTool Android app (XCS) but have not been tested with real hardware. Community feedback is welcome!
45+
46+
## Installation
47+
48+
### HACS (Recommended)
49+
50+
[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=thecodingdad&repository=ha-xtool&category=integration)
51+
52+
Or add manually:
53+
1. Open HACS in your Home Assistant instance
54+
2. Click the three dots in the top right corner and select **Custom repositories**
55+
3. Enter `https://github.com/thecodingdad/ha-xtool` and select **Integration** as the category
56+
4. Click **Add**, then search for "xTool Laser" and download it
57+
5. Restart Home Assistant
58+
59+
### Manual Installation
60+
61+
1. Download the latest release from [GitHub Releases](https://github.com/thecodingdad/ha-xtool/releases)
62+
2. Copy the `custom_components/xtool` folder to your Home Assistant `config/custom_components/` directory
63+
3. Restart Home Assistant
64+
65+
## Configuration
66+
67+
### Adding a Device
68+
69+
1. Go to **Settings** > **Devices & Services** > **Add Integration**
70+
2. Search for **xTool Laser**
71+
3. The integration scans your network for xTool devices via UDP broadcast
72+
4. Select a discovered device from the list, or choose **Enter IP address manually**
73+
5. Confirm to add the device
74+
75+
### Options
76+
77+
After adding a device, you can configure it under **Settings** > **Devices & Services** > **xTool Laser** > **Configure**:
78+
79+
| Option | Description |
80+
|--------|-------------|
81+
| Power switch | Link an existing switch entity (e.g. a smart plug) that controls the laser's power supply. When the switch is off, the status shows "Off" and entities become unavailable. |
82+
83+
## Entities
84+
85+
> **Note:** Not all entities are available on every model. The integration automatically detects your device model and connected accessories, and only creates entities for supported features.
86+
87+
### Sensors
88+
89+
| Entity | Description |
90+
|--------|-------------|
91+
| Status | Device status: Off, Idle, Processing, Paused, Finished, Error, etc. Always available — shows "Off" when device is unreachable |
92+
| Laser position X/Y/Z | Current laser head position in mm |
93+
| Fire level | Current flame detection level |
94+
| Air assist level | Current air assist gear (0-4) |
95+
| Task ID | Currently loaded job identifier |
96+
| Task time | Current job elapsed time |
97+
| Working time | Lifetime working hours (total_increasing) |
98+
| Session count | Total number of job starts (total_increasing) |
99+
| Standby time | Lifetime standby hours (total_increasing) |
100+
| Tool runtime | Current laser module runtime hours (total_increasing) |
101+
102+
### Diagnostic Sensors
103+
104+
| Entity | Description |
105+
|--------|-------------|
106+
| IP address | Device IP address |
107+
| Laser power | Laser module power in watts |
108+
| Laser module | Human-readable laser description (e.g. "20W Diode") |
109+
| SD card | SD card inserted / not inserted |
110+
111+
### Light
112+
113+
| Entity | Description |
114+
|--------|-------------|
115+
| Fill light | Dimmable work light (0-100%) |
116+
117+
### Switches
118+
119+
Not all entities are available on every model. Entities are only created when supported by the connected device.
120+
121+
| Entity | Description |
122+
|--------|-------------|
123+
| Flame alarm | Enable/disable fire detection |
124+
| Buzzer | Enable/disable audio feedback |
125+
| Move stop | Enable/disable emergency movement stop |
126+
| Exhaust fan | Enable/disable smoke extraction fan |
127+
| Power | Controls the linked smart plug (only when configured in options) |
128+
129+
### Numbers
130+
131+
| Entity | Range | Unit | Description |
132+
|--------|-------|------|-------------|
133+
| Air assist delay | 0-600 | seconds | Time the air assist stays on after processing |
134+
| Exhaust fan duration | 1-600 | seconds | Smoke extraction duration |
135+
136+
### Select
137+
138+
| Entity | Options | Description |
139+
|--------|---------|-------------|
140+
| Flame alarm sensitivity | High, Low | Flame detection sensitivity level |
141+
142+
### Buttons
143+
144+
| Entity | Description |
145+
|--------|-------------|
146+
| Pause job | Pause the current processing job |
147+
| Resume job | Resume a paused job |
148+
| Cancel job | Cancel and stop the current job |
149+
| Home all axes | Move laser head to home position (all axes) |
150+
| Home XY | Move laser head to XY home position |
151+
| Home Z | Move laser head to Z home position |
152+
153+
### Binary Sensors
154+
155+
| Entity | Description |
156+
|--------|-------------|
157+
| Accessories | On when accessories are attached. Attributes list the connected accessories (Air Pump, Fire Extinguisher, Riser Base) with firmware versions |
158+
159+
### Cameras (P2/P2S only)
160+
161+
| Entity | Description |
162+
|--------|-------------|
163+
| Overview camera | Wide-angle workspace camera |
164+
| Close-up camera | Detailed close-up camera |
165+
166+
## Device Information
167+
168+
The device page in Home Assistant shows:
169+
170+
- **Manufacturer:** xTool
171+
- **Model:** e.g. xTool S1
172+
- **Serial Number:** Device serial number
173+
- **Firmware:** firmware version
174+
175+
## Using XCS alongside Home Assistant
176+
177+
The xTool Creative Space (XCS) desktop app and this integration can be used in parallel. When the XCS app is detected (it causes WebSocket disconnects), the integration automatically switches to **XCS Compatibility Mode**:
178+
179+
- Commands fall back to HTTP
180+
- Entity states are preserved from the last successful poll
181+
- Failed commands are prioritized in the next poll cycle (rotating command order)
182+
- Periodic recovery attempts restore the WebSocket connection when XCS is closed
183+
184+
## Technical Details
185+
186+
This integration communicates with xTool devices using protocols discovered through reverse engineering of the xTool XCS Android app:
187+
188+
- **UDP port 20000** — Device discovery via broadcast (`{"requestId": <int>}`)
189+
- **WebSocket port 8081** (S1) — Real-time G-code command/response with push state updates
190+
- **HTTP port 8080** — System queries, file upload, command fallback
191+
- **REST API port 8080** (F1/P2/M1) — JSON REST endpoints for device control
192+
193+
Commands use a G-code dialect (M-codes): `M222` (status), `M13` (light), `M340` (flame alarm), `M2003` (full device info), etc.
194+
195+
## Multilanguage Support
196+
197+
This integration supports English and German.
198+
199+
## License
200+
201+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""The xTool Laser integration."""
2+
3+
from __future__ import annotations
4+
5+
from homeassistant.config_entries import ConfigEntry
6+
from homeassistant.const import CONF_HOST, Platform
7+
from homeassistant.core import HomeAssistant
8+
9+
from .const import CONF_POWER_SWITCH, DEFAULT_DEVICE_NAME, DOMAIN
10+
from .coordinator import XtoolCoordinator
11+
from .http_mcode_protocol import HttpMcodeProtocol
12+
from .models import detect_model
13+
from .protocol import LaserInfo, ProtocolType, XtoolProtocol
14+
from .rest_protocol import RestProtocol
15+
from .ws_protocol import WsMcodeProtocol
16+
17+
PLATFORMS = [
18+
Platform.BINARY_SENSOR,
19+
Platform.CAMERA,
20+
Platform.SENSOR,
21+
Platform.LIGHT,
22+
Platform.SWITCH,
23+
Platform.NUMBER,
24+
Platform.SELECT,
25+
Platform.BUTTON,
26+
]
27+
28+
type XtoolConfigEntry = ConfigEntry[XtoolCoordinator]
29+
30+
31+
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
32+
"""Set up the xTool integration."""
33+
return True
34+
35+
36+
def _create_protocol(host: str, protocol_type: str) -> XtoolProtocol:
37+
"""Create the correct protocol instance based on the detected type."""
38+
if protocol_type == ProtocolType.WS_MCODE:
39+
return WsMcodeProtocol(host)
40+
if protocol_type == ProtocolType.HTTP_MCODE:
41+
return HttpMcodeProtocol(host)
42+
return RestProtocol(host)
43+
44+
45+
async def async_setup_entry(hass: HomeAssistant, entry: XtoolConfigEntry) -> bool:
46+
"""Set up xTool Laser from a config entry."""
47+
host = entry.data[CONF_HOST]
48+
device_name = entry.data.get("device_name", DEFAULT_DEVICE_NAME)
49+
serial_number = entry.data.get("serial_number", "")
50+
firmware_version = entry.data.get("firmware_version", "")
51+
protocol_type = entry.data.get("protocol_type", ProtocolType.WS_MCODE)
52+
model = detect_model(device_name)
53+
54+
power_switch_entity_id = entry.options.get(CONF_POWER_SWITCH)
55+
56+
protocol = _create_protocol(host, protocol_type)
57+
58+
coordinator = XtoolCoordinator(
59+
hass,
60+
protocol=protocol,
61+
device_name=device_name,
62+
serial_number=serial_number,
63+
firmware_version=firmware_version,
64+
model=model,
65+
power_switch_entity_id=power_switch_entity_id,
66+
)
67+
laser_power_watts = entry.data.get("laser_power_watts", 0)
68+
if laser_power_watts:
69+
coordinator.laser = LaserInfo(power_watts=laser_power_watts)
70+
71+
await coordinator.async_config_entry_first_refresh()
72+
73+
entry.runtime_data = coordinator
74+
75+
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
76+
77+
entry.async_on_unload(entry.add_update_listener(_async_options_updated))
78+
79+
return True
80+
81+
82+
async def async_unload_entry(hass: HomeAssistant, entry: XtoolConfigEntry) -> bool:
83+
"""Unload a config entry."""
84+
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
85+
await entry.runtime_data.async_shutdown()
86+
return unload_ok
87+
88+
89+
async def _async_options_updated(
90+
hass: HomeAssistant, entry: XtoolConfigEntry
91+
) -> None:
92+
"""Reload integration when options change."""
93+
await hass.config_entries.async_reload(entry.entry_id)

0 commit comments

Comments
 (0)