Skip to content

Commit

Permalink
[MiHome] Add a Bridge property for the network interface to be used f…
Browse files Browse the repository at this point in the history
…or multicast traffic (openhab#9619)

Signed-off-by: Ion Marusic <ion.marusic@gmail.com>
  • Loading branch information
ratza authored and thinkingstone committed Nov 7, 2021
1 parent 089a144 commit 7f44874
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 12 deletions.
32 changes: 24 additions & 8 deletions bundles/org.openhab.binding.mihome/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The sensors run on a coin cell battery for over a year.

After setup, you can disconnect the gateway from the internet to keep your sensor information private.

Please note that using the Xiaomi gateway with OpenHAB requires enabling the developer mode and that multiple user reports suggest that it is no longer posible.
Please note that using the Xiaomi gateway with openHAB requires enabling the developer mode and that multiple user reports suggest that it is no longer possible.
Zigbee2Mqtt provides an alternative method to integrate Xiaomi devices.

## Supported devices
Expand Down Expand Up @@ -102,21 +102,25 @@ __Hints:__

## Removing devices from the gateway

If you remove a Thing in PapaerUI it will also trigger the gateway to unpair the device.
If you remove a Thing in PaperUI it will also trigger the gateway to unpair the device.
It will only reappear in your Inbox, if you connect it to the gateway again.
Just follow the instructions in ["Connecting devices to the gateway"](#connecting-devices-to-the-gateway).

## Network configuration

- The binding requires port `9898` to not be used by any other service on the system.
- Make sure multicast traffic is correctly routed between the gateway and your openHAB instance
- To correctly receive multicast traffic, when your openHAB machine is using multiple network interfaces, you might need to configure the optional `interface` property on the `Bridge` Thing, like so:
```
Bridge mihome:bridge:f0b429XXXXXX "Xiaomi Gateway" [ ..., interface="eth0", ... ] {
```

## Configuration examples

### xiaomi.things:

```
Bridge mihome:bridge:f0b429XXXXXX "Xiaomi Gateway" [ serialNumber="f0b429XXXXXX", ipAddress="192.168.0.3", port=9898, key="XXXXXXXXXXXXXXXX", pollingInterval=6000 ] {
Bridge mihome:bridge:f0b429XXXXXX "Xiaomi Gateway" [ serialNumber="f0b429XXXXXX", ipAddress="192.168.0.3", port=9898, key="XXXXXXXXXXXXXXXX" ] {
Things:
gateway f0b429XXXXXX "Xiaomi Mi Smart Home Gateway" [itemId="f0b429XXXXXX"]
sensor_ht 158d0001XXXXXX "Xiaomi Temperature Sensor" [itemId="158d0001XXXXXX"]
Expand Down Expand Up @@ -452,10 +456,10 @@ Make sure you have connected your gateway to openHAB and the communication is wo
The devices send different types of messages to the gateway.
You have to capture as many of them as possible, so that the device is fully supported in the end.

1. Heartbeat (transmitted usually every 60 minutes)
1. Heartbeat (usually transmitted every 60 minutes)
2. Report (device reports new sensor or status values)
3. Read ACK (binding refreshes all sensor values after a restart of openHAB)
4. Write ACK (device has received a command) __not avaiable for sensor-only devices__
4. Write ACK (device has received a command) __not available for sensor-only devices__

### Open a new issue or get your hands dirty

Expand All @@ -465,7 +469,7 @@ Post an issue in the GitHub repository with as much information as possible abou
- model name
- content of all the different message types

Or implement the support by youself and submit a pull request.
Or implement the support by yourself and submit a pull request.

### Handle the message contents of a basic device thing with items

Expand All @@ -484,7 +488,7 @@ _Example for the same message from the heartbeat channel - only the data is retu

These messages are in JSON format, which also gives you the ability to parse single values.

_Example for the retrieved IP from the heartbeat message and transformed with JSONPATH transfomration: ```String Gateway_IP {channel="mihome:basic:xxx:heartbeatMessage"[profile="transform:JSONPATH", function="$.ip"]}```_
_Example for the retrieved IP from the heartbeat message and transformed with JSONPATH transformation: ```String Gateway_IP {channel="mihome:basic:xxx:heartbeatMessage"[profile="transform:JSONPATH", function="$.ip"]}```_

The item will get the value `192.168.0.124`.

Expand Down Expand Up @@ -545,7 +549,18 @@ In case you want to check if the communication between the machine and the gatew
- make sure you have __netcat__ installed
- Enter ```netcat -ukl 9898```
- At least every 10 seconds you should see a message coming in from the gateway which looks like
```{"cmd":"heartbeat","model":"gateway","sid":"`xxx","short_id":"0","token":"xxx","data":"{\"ip\":\"`xxx\"}"}```
```{"cmd":"heartbeat","model":"gateway","sid":"`xxx","short_id":"0","token":"xxx","data":"{\"ip\":\"`xxx\"}"}```

#### Multiple network interfaces

When the computer running openHAB has more than one network interface configured (typically, a VLAN for your segregated IoT devices, and the other for your regular traffic like internet, openHAB panel access, etc), it could be that openHAB will attempt to listen for Multicast traffic of the Gateway on the wrong network interface. That will prevent openHAB and `netcat` from receiving the messages from the Xiaomi Gateway. Within openHAB this manifests by seeing the Gateway and its devices online for a brief period after openHAB startup, after which they timeout and are shown Offline. No channel triggers from the Gateway work in this case.

In order to verify that traffic is actually received by the machine use `tcpdump` on each interface:
- List your network interfaces `ifconfig | grep MULTICAST` or `ip link | grep MULTICAST`
- Use `tcpdump -i <interface> port 9898` for each interface to verify if you receive traffic

If you already know the correct interface, or you found the correct one through tcpdump:
- Configure the `interface` property of the `Bridge` Thing with the correct name (for example `eth0`, etc)

### Check if your Windows/Mac machine receives multicast traffic

Expand All @@ -562,6 +577,7 @@ __My gateway shows up in openHAB and I have added all devices, but I don't get a
- Make sure the gateway and the machine are in the same subnet
- Try to connect your machine via Ethernet instead of Wifi
- Make sure you don't have any firewall rules blocking multicast
- If you have multiple network interfaces, try to configure the `interface` property of the `Bridge` Thing

__I have connected my gateway to the network but it doesn't show up in openHAB:__
- Make sure to have the developer mode enabled in the MiHome app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public class XiaomiGatewayBindingConstants {
public static final String SERIAL_NUMBER = "serialNumber";
public static final String HOST = "ipAddress";
public static final String PORT = "port";
public static final String INTERFACE = "interface";
public static final String TOKEN = "token";

// Item config properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public void initialize() {
return;
}
logger.debug("Init socket on Port: {}", port);
socket = new XiaomiBridgeSocket(port, getThing().getUID().getId());
socket = new XiaomiBridgeSocket(port, (String) config.get(INTERFACE), getThing().getUID().getId());
socket.initialize();
socket.registerListener(this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -30,9 +32,11 @@
public class XiaomiBridgeSocket extends XiaomiSocket {

private final Logger logger = LoggerFactory.getLogger(XiaomiBridgeSocket.class);
private @Nullable final String netIf;

public XiaomiBridgeSocket(int port, String owner) {
public XiaomiBridgeSocket(int port, String netIf, String owner) {
super(port, owner);
this.netIf = netIf;
}

/**
Expand All @@ -51,10 +55,16 @@ protected synchronized void setupSocket() {
try {
logger.debug("Setup socket");
socket = new MulticastSocket(getPort());

if (netIf != null) {
socket.setNetworkInterface(NetworkInterface.getByName(netIf));
}

setSocket(socket); // must bind receive side
socket.joinGroup(InetAddress.getByName(MCAST_ADDR));
logger.debug("Initialized socket to {}:{} on {}:{}", socket.getRemoteSocketAddress(), socket.getPort(),
socket.getLocalAddress(), socket.getLocalPort());
logger.debug("Initialized socket to {}:{} on {}:{} bound to {} network interface",
socket.getRemoteSocketAddress(), socket.getPort(), socket.getLocalAddress(), socket.getLocalPort(),
socket.getNetworkInterface());
} catch (IOException e) {
logger.error("Setup socket error", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@
<advanced>true</advanced>
</parameter>

<parameter name="interface" type="text">
<label>Interface</label>
<description>Interface to bind to for the MiHome communication channel</description>
<required>false</required>
<advanced>true</advanced>
</parameter>

<parameter name="key" type="text">
<label>Developer Key</label>
<description>Developer key extracted from Xiaomi's app</description>
Expand Down

0 comments on commit 7f44874

Please sign in to comment.