homebridge-samsung-rac 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/config.schema.json +85 -0
- package/dist/cli/probe.d.ts +24 -0
- package/dist/cli/probe.js +475 -0
- package/dist/cli/probe.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.js +49 -0
- package/dist/config.js.map +1 -0
- package/dist/deviceAdapter.d.ts +62 -0
- package/dist/deviceAdapter.js +186 -0
- package/dist/deviceAdapter.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +23 -0
- package/dist/platform.js +163 -0
- package/dist/platform.js.map +1 -0
- package/dist/platformAccessory.d.ts +105 -0
- package/dist/platformAccessory.js +515 -0
- package/dist/platformAccessory.js.map +1 -0
- package/dist/racStatus.d.ts +81 -0
- package/dist/racStatus.js +61 -0
- package/dist/racStatus.js.map +1 -0
- package/dist/settings.d.ts +14 -0
- package/dist/settings.js +18 -0
- package/dist/settings.js.map +1 -0
- package/dist/transport/atomic.d.ts +11 -0
- package/dist/transport/atomic.js +72 -0
- package/dist/transport/atomic.js.map +1 -0
- package/dist/transport/certificate.d.ts +35 -0
- package/dist/transport/certificate.js +123 -0
- package/dist/transport/certificate.js.map +1 -0
- package/dist/transport/httpOverTls.d.ts +45 -0
- package/dist/transport/httpOverTls.js +176 -0
- package/dist/transport/httpOverTls.js.map +1 -0
- package/dist/transport/localApi.d.ts +55 -0
- package/dist/transport/localApi.js +125 -0
- package/dist/transport/localApi.js.map +1 -0
- package/dist/transport/pairing.d.ts +51 -0
- package/dist/transport/pairing.js +215 -0
- package/dist/transport/pairing.js.map +1 -0
- package/dist/transport/tls.d.ts +25 -0
- package/dist/transport/tls.js +46 -0
- package/dist/transport/tls.js.map +1 -0
- package/dist/transport/tokenStore.d.ts +27 -0
- package/dist/transport/tokenStore.js +90 -0
- package/dist/transport/tokenStore.js.map +1 -0
- package/homebridge-ui/public/index.html +392 -0
- package/homebridge-ui/server.js +198 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# homebridge-samsung-rac
|
|
2
|
+
|
|
3
|
+
Control a Samsung room air conditioner from HomeKit over its **local network API**
|
|
4
|
+
(`https://<ip>:8888`, mutual TLS) instead of the SmartThings cloud.
|
|
5
|
+
|
|
6
|
+
The local API is richer than the cloud one. Most importantly it exposes the vane
|
|
7
|
+
position — `Wind.direction` — which the SmartThings API does not expose at all,
|
|
8
|
+
and which is the reason this plugin exists.
|
|
9
|
+
|
|
10
|
+
Developed against a **TP6X_RAC_16K** (SmartThings vendor id `DA-AC-RAC-100001`).
|
|
11
|
+
Other units of the same generation should work; older port-2878 models will not.
|
|
12
|
+
|
|
13
|
+
## What it exposes
|
|
14
|
+
|
|
15
|
+
A single `HeaterCooler` accessory per unit:
|
|
16
|
+
|
|
17
|
+
| HomeKit | Comes from |
|
|
18
|
+
|---|---|
|
|
19
|
+
| On / off | `Operation.power` |
|
|
20
|
+
| Current temperature | `Temperatures[0].current` |
|
|
21
|
+
| Target temperature | `Temperatures[0].desired`, with the range the unit reports |
|
|
22
|
+
| Mode | `Mode.modes`, limited to the modes the unit says it supports |
|
|
23
|
+
| Fan speed | `Wind.speedLevel` / `Wind.maxSpeedLevel` |
|
|
24
|
+
| Swing | `Wind.direction` |
|
|
25
|
+
| Filter indicator | the `FilterAlarm` entry in `Alarms` |
|
|
26
|
+
|
|
27
|
+
Dry and fan-only modes have no HomeKit equivalent. The plugin reports them as
|
|
28
|
+
Auto/Idle and leaves them alone rather than overwriting a mode you chose in the
|
|
29
|
+
Samsung app.
|
|
30
|
+
|
|
31
|
+
A service only appears once the unit has actually published a reading for it, and
|
|
32
|
+
is never withdrawn afterwards — some units publish nothing until they are running.
|
|
33
|
+
|
|
34
|
+
## Setup
|
|
35
|
+
|
|
36
|
+
1. Install the plugin and open its settings in the Homebridge UI.
|
|
37
|
+
2. **Fetch the certificate.** The local API requires a client certificate that
|
|
38
|
+
bundles a private key. It is not shipped with this plugin: it is downloaded
|
|
39
|
+
once and stored next to your Homebridge config, on your machine only.
|
|
40
|
+
3. **Add your air conditioner's IP address** and save. Give it a fixed DHCP lease
|
|
41
|
+
on your router — the plugin cannot follow a unit that moves. You can add
|
|
42
|
+
several units.
|
|
43
|
+
4. **Pair.** This is the awkward part, and the order matters:
|
|
44
|
+
- power the air conditioner **off**
|
|
45
|
+
- press *Start pairing*
|
|
46
|
+
- power it back **on** when the UI asks
|
|
47
|
+
|
|
48
|
+
The unit does not hand out a token on request. It calls back to Homebridge on
|
|
49
|
+
port 8889 the moment it powers on, and that callback carries the token.
|
|
50
|
+
|
|
51
|
+
The token is stored alongside the certificate, outside `config.json`.
|
|
52
|
+
|
|
53
|
+
### If pairing never completes
|
|
54
|
+
|
|
55
|
+
The air conditioner connects back to whatever address asked it for a token, so
|
|
56
|
+
the callback has to be able to reach Homebridge:
|
|
57
|
+
|
|
58
|
+
- **Homebridge in Docker without host networking** — the callback cannot arrive.
|
|
59
|
+
Pair from the host with the probe CLI below and paste the token into the
|
|
60
|
+
*Paste token* box:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx homebridge-samsung-rac-probe pair --host 10.0.0.9
|
|
64
|
+
```
|
|
65
|
+
- **A firewall on the Homebridge machine** — allow inbound TCP on port 8889.
|
|
66
|
+
- **A listener left over from an earlier attempt** — it keeps the port and the
|
|
67
|
+
next run fails in a way that looks identical to the unit never calling back.
|
|
68
|
+
|
|
69
|
+
## Probe CLI
|
|
70
|
+
|
|
71
|
+
A command-line driver over the plugin's own transport code, for setting a unit up
|
|
72
|
+
without the UI and for working out what a misbehaving one is doing.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx homebridge-samsung-rac-probe cert # download the certificate
|
|
76
|
+
npx homebridge-samsung-rac-probe pair --host 10.0.0.9 # run the pairing ritual
|
|
77
|
+
npx homebridge-samsung-rac-probe dump --host 10.0.0.9 # print the device state
|
|
78
|
+
npx homebridge-samsung-rac-probe writes --host 10.0.0.9 --power-on
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
It ships with the plugin, so it works from an ordinary install. From a checkout,
|
|
82
|
+
`npm run probe -- dump --host 10.0.0.9` runs the same thing through ts-node.
|
|
83
|
+
|
|
84
|
+
Progress goes to stderr and the payload to stdout, so either can be redirected
|
|
85
|
+
without the running commentary landing in the file:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx homebridge-samsung-rac-probe dump --host 10.0.0.9 > unit.json
|
|
89
|
+
npx homebridge-samsung-rac-probe writes --host 10.0.0.9 --power-on > report.md
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`dump` is the first thing to ask for in a bug report about an unfamiliar model:
|
|
93
|
+
it separates a parsing bug from a genuine hardware difference, and its stdout is
|
|
94
|
+
the device's own JSON.
|
|
95
|
+
|
|
96
|
+
`writes` matters more than it sounds. **This hardware answers HTTP 200 to commands
|
|
97
|
+
it silently discards.** The probe writes a value, reads it back, and counts only a
|
|
98
|
+
*changed read-back* as success. It restores everything it touched and leaves a
|
|
99
|
+
report, printed to stdout (`--out <path>` saves it to a file instead). The
|
|
100
|
+
findings for the reference unit are summarised below.
|
|
101
|
+
|
|
102
|
+
Always pass `--power-on`. On the reference unit **every write except power is
|
|
103
|
+
silently discarded while it is off** — 200, no error, no effect — so a run without
|
|
104
|
+
it says nothing about whether a field is writable.
|
|
105
|
+
|
|
106
|
+
The plugin applies the same rule at runtime: every write is confirmed by a
|
|
107
|
+
read-back, and a value the unit quietly refuses is corrected in the Home app
|
|
108
|
+
rather than left showing something that never happened.
|
|
109
|
+
|
|
110
|
+
### What the reference unit accepts
|
|
111
|
+
|
|
112
|
+
Measured on a TP6X_RAC_16K, 2026-09-15:
|
|
113
|
+
|
|
114
|
+
| | |
|
|
115
|
+
|---|---|
|
|
116
|
+
| Method | `PUT` only — `POST` returns 405 |
|
|
117
|
+
| Power, fan speed, setpoint, mode | all applied |
|
|
118
|
+
| Vane | `Fix` and `Up_And_Low` applied; `Vertical` and `SwingUD` rejected with 400; `All` accepted and then ignored |
|
|
119
|
+
|
|
120
|
+
Consequence worth knowing: **changing anything in the Home app while the unit is
|
|
121
|
+
off does nothing**, and the tile will snap back after a second. Turn it on first.
|
|
122
|
+
That is the hardware's behaviour, not the plugin's — the plugin reports it
|
|
123
|
+
honestly rather than pretending the change stuck.
|
|
124
|
+
|
|
125
|
+
## Configuration
|
|
126
|
+
|
|
127
|
+
Everything is editable in the Homebridge UI. The equivalent `config.json`:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"platform": "SamsungRacLocal",
|
|
132
|
+
"devices": [
|
|
133
|
+
{ "name": "Lounge", "host": "10.0.0.9" }
|
|
134
|
+
],
|
|
135
|
+
"updateInterval": 10,
|
|
136
|
+
"swingDirection": "Up_And_Low"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
| Key | Default | |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `devices[].host` | — | The unit's IP address. Required. |
|
|
143
|
+
| `devices[].name` | the unit's own name | Shown in the Home app. |
|
|
144
|
+
| `devices[].token` | — | Only if you paired outside this plugin. |
|
|
145
|
+
| `updateInterval` | `10` | Seconds between polls; minimum 5. |
|
|
146
|
+
| `swingDirection` | `Up_And_Low` | What to set the vane to for "swing on". Units differ; the log says if yours ignores it. |
|
|
147
|
+
| `requestTimeout` | `5` | Seconds. |
|
|
148
|
+
| `certificateUrl` | community URL | Only if you mirror the certificate yourself. |
|
|
149
|
+
|
|
150
|
+
## Security notes
|
|
151
|
+
|
|
152
|
+
- The client certificate contains a private key and is **not** in this
|
|
153
|
+
repository. It is fetched at setup time and written `0600` under your
|
|
154
|
+
Homebridge storage path.
|
|
155
|
+
- Device tokens grant full local control of the air conditioner. They are stored
|
|
156
|
+
`0600` in the same directory, not in `config.json`.
|
|
157
|
+
- The connection to the unit uses TLS 1.0 with OpenSSL security level 0, because
|
|
158
|
+
that is all its firmware speaks. It never leaves your network.
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm install
|
|
164
|
+
npm test
|
|
165
|
+
npm run lint
|
|
166
|
+
npm run build
|
|
167
|
+
npm run watch # rebuild and restart Homebridge on change
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Unit tests never touch the network: `nock` is locked down and `fetch` is replaced
|
|
171
|
+
outright. The fixture in `tests/fixtures/devices.json` is a real capture from the
|
|
172
|
+
reference unit.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pluginAlias": "SamsungRacLocal",
|
|
3
|
+
"pluginType": "platform",
|
|
4
|
+
"singular": true,
|
|
5
|
+
"customUi": true,
|
|
6
|
+
"headerDisplay": "Controls a Samsung room air conditioner over its local network API. Add each unit's IP address below, then pair it from the card above — pairing needs the unit powered off and back on, so it cannot be done from this form alone.",
|
|
7
|
+
"schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"devices": {
|
|
11
|
+
"title": "Air conditioners",
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"name": {
|
|
17
|
+
"title": "Name",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Shown in the Home app. Leave empty to use the name the unit reports."
|
|
20
|
+
},
|
|
21
|
+
"host": {
|
|
22
|
+
"title": "IP address",
|
|
23
|
+
"type": "string",
|
|
24
|
+
"required": true,
|
|
25
|
+
"format": "ipv4",
|
|
26
|
+
"description": "The unit's address on your network. Give it a fixed DHCP lease on your router — the plugin cannot follow it if it moves."
|
|
27
|
+
},
|
|
28
|
+
"token": {
|
|
29
|
+
"title": "Device token (optional)",
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Only needed if you paired outside this plugin. Leave empty and use Pair above; tokens paired there are stored outside config.json."
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"updateInterval": {
|
|
37
|
+
"title": "Status poll interval (seconds)",
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"default": 10,
|
|
40
|
+
"minimum": 5,
|
|
41
|
+
"description": "How often to read the unit. This is a local network request, so it can be frequent."
|
|
42
|
+
},
|
|
43
|
+
"swingDirection": {
|
|
44
|
+
"title": "Swing direction",
|
|
45
|
+
"type": "string",
|
|
46
|
+
"default": "Up_And_Low",
|
|
47
|
+
"oneOf": [
|
|
48
|
+
{ "title": "Up and down (confirmed working)", "enum": ["Up_And_Low"] },
|
|
49
|
+
{ "title": "All directions", "enum": ["All"] },
|
|
50
|
+
{ "title": "Vertical", "enum": ["Vertical"] },
|
|
51
|
+
{ "title": "Swing up/down", "enum": ["SwingUD"] }
|
|
52
|
+
],
|
|
53
|
+
"description": "What to set the vane to when swing is switched on in HomeKit. On a TP6X_RAC_16K only Up_And_Low works: Vertical and SwingUD are rejected outright, and All is accepted and then ignored. The others are kept for units that differ; the log says if yours refuses the one you pick."
|
|
54
|
+
},
|
|
55
|
+
"requestTimeout": {
|
|
56
|
+
"title": "Request timeout (seconds)",
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"default": 5,
|
|
59
|
+
"minimum": 1
|
|
60
|
+
},
|
|
61
|
+
"certificateUrl": {
|
|
62
|
+
"title": "Client certificate URL",
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "Advanced. Where to download the Samsung client certificate from. Change this only if you mirror it yourself."
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"layout": [
|
|
69
|
+
{
|
|
70
|
+
"key": "devices",
|
|
71
|
+
"type": "array",
|
|
72
|
+
"buttonText": "Add an air conditioner",
|
|
73
|
+
"items": ["devices[].name", "devices[].host", "devices[].token"]
|
|
74
|
+
},
|
|
75
|
+
"updateInterval",
|
|
76
|
+
"swingDirection",
|
|
77
|
+
{
|
|
78
|
+
"type": "fieldset",
|
|
79
|
+
"title": "Advanced",
|
|
80
|
+
"expandable": true,
|
|
81
|
+
"expanded": false,
|
|
82
|
+
"items": ["requestTimeout", "certificateUrl"]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Command-line driver over the plugin's own transport modules.
|
|
4
|
+
*
|
|
5
|
+
* It answered the question this plugin was built around — does the hardware
|
|
6
|
+
* apply writes, or accept and discard them the way the SmartThings cloud does? —
|
|
7
|
+
* and stays as the tool to reach for when the plugin meets real hardware:
|
|
8
|
+
*
|
|
9
|
+
* - `dump` tells a parsing bug apart from a hardware difference when someone
|
|
10
|
+
* reports a model that behaves unlike the reference unit;
|
|
11
|
+
* - `writes` establishes what a different model accepts, which matters because
|
|
12
|
+
* the accepted values ARE model-specific (this unit rejects `Vertical`
|
|
13
|
+
* outright while accepting `Up_And_Low`);
|
|
14
|
+
* - `pair` is the way through when Homebridge runs in a container that the air
|
|
15
|
+
* conditioner's callback cannot reach.
|
|
16
|
+
*
|
|
17
|
+
* Shipped as a bin, so it is reachable from an ordinary install rather than only
|
|
18
|
+
* from a checkout:
|
|
19
|
+
*
|
|
20
|
+
* npx homebridge-samsung-rac-probe dump --host 172.24.0.125
|
|
21
|
+
*
|
|
22
|
+
* From a checkout, `npm run probe -- dump --host ...` runs it through ts-node.
|
|
23
|
+
*/
|
|
24
|
+
export {};
|