Skip to content

sergey-brutsky/mi-home

Repository files navigation

C# Library for using xiaomi smart gateway in your automation scenarious

Build project Tests Nuget License

This library provides simple and flexible C# API for Xiaomi Mi Home devices.

Currently supports only Gateway version 2 (DGNWG02LM), Air Humidifier (zhimi.humidifier.v1), Mi Robot vacuum (rockrobo.vacuum.v1) and several sensors. See the pictures below.

smart-home

humidifier mirobot

gateway temperature_sensor socket_plug motion_sensor motion_sensor_2 door_window_sensor aqara_door_window_sensor water_sensor smoke_sensor switch wired wall switch sensor_weather wireless dual wall switch aqara_cube_sensor

Table of Contents

  1. Installation
  2. Setup Gateway
  3. Basic scenario
  4. Supported devices

via nuget package manager

Install-Package MiHomeLib

or

dotnet add package MiHomeLib

or install via GitHub packages

Before using this library you should setup development mode on your gateway, instructions how to do this.
This mode allows to work with the gateway via UDP multicast protocol.

Warning 1: If you bought a newer revision of Mi Home Gateway (labels in a circle)

It could be possible that ports on your gateway required for UDP multicast traffic are closed.
Before using this library ports must be opened. Check this instruction.

Warning 2: Mi Home Gateway uses udp multicast for messages handling, so your app must be hosted in the same LAN as your gateway. If it is not you have to use multicast routers like udproxy or igmpproxy or vpn bridging.

Warning 3: If your app is running on windows machine, make sure that you disabled virtual network adapters like VirtualBox, Hyper-V, Npcap, pcap etc. Because these adapters may prevent proper work of multicast traffic between your machine and gateway

Get all devices in the network

public static void Main(string[] args)
{
    // gateway password is optional, needed only to send commands to your devices
    // gateway sid is optional, use only when you have 2 gateways in your LAN
    // using var miHome = new MiHome("gateway password", "gateway sid");
    using var miHome = new MiHome();
   
    miHome.OnAnyDevice += (_, device) =>
    {
        Console.WriteLine($"{device.Sid}, {device.GetType()}, {device}"); // all discovered devices
    };

    Console.ReadLine();
}

gateway

using var miHome = new MiHome("gateway password here"); // here we using developers api 

miHome.OnGateway += (_, gateway) =>
{
    gateway.EnableLight(); // by default this is "white" light
    Task.Delay(3000).Wait();
    gateway.DisableLight(); // light off
    Task.Delay(3000).Wait();
    gateway.EnableLight(255, 0, 0, 100); // turn on "red" light with full brightness 
    Task.Delay(3000).Wait();
    gateway.DisableLight(); // light off
    Task.Delay(3000).Wait();
    gateway.PlaySound(Gateway.Sound.IceWorldPiano, 50); // play ice world piano sound on gateway with volume 50%
    Task.Delay(3000).Wait();
    gateway.SoundsOff();
    gateway.PlayCustomSound(10_002, 50); // play custom sound with volume 50%
    Task.Delay(3000).Wait();
    gateway.SoundsOff();

};

Yes, it is possible to upload custom sounds to your gateway and use them in various scenarios. Check this instruction.

It is possible to add/remove/play custom radio channels in this version of gateway.

Bellow is a simple code snippet explaining how to use this feature.

var gw = new MiioGateway("192.168.1.12", "<your gateway token here>");

var radioChannels = gw.GetRadioChannels(); // get list of available custom radio channels

foreach (var channel in radioChannels)
{
    Console.WriteLine(channel);
}

gw.AddRadioChannel(1025, "http://192.168.1.1/my-playlist.m3u8"); // add custom radio channel
Task.Delay(1000).Wait();
gw.PlayRadio(1024, 50); // play newly-added channel with volume 50%
Task.Delay(1000).Wait();
gw.StopRadio(); // stop playing radio
Task.Delay(1000).Wait();
gw.RemoveRadioChannel(1024); // remove newly-added channel
Task.Delay(1000).Wait();
gw.RemoveAllRadioChannels(); // remove all custom radio channels

Async methods also supported.

Warning 1: Added radio channels are not persistant. Gateway may remove them from time to time. Warning 2: My gateway recognizes only songs in aac format (mp3 is not supported).

Here is minimal working sample of m3u8 file that gateway recognizes and respects.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:240,
http://192.168.1.2/test.aac

EXT-X-MEDIA-SEQUENCE - number of songs in your playlist.

EXTINF - track length in seconds.

http://192.168.1.2/test.aac - url to your song

temperature_sensor

using var miHome = new MiHome();

miHome.OnThSensor += (_, thSensor) =>
{
    if (thSensor.Sid == "158d000182dfbc") // sid of specific device
    {
        Console.WriteLine(thSensor); // Sample output --> Temperature: 22,19°C, Humidity: 74,66%, Voltage: 3,035V

        thSensor.OnTemperatureChange += (_, e) =>
        {
            Console.WriteLine($"New temperature: {e.Temperature}");
        };

        thSensor.OnHumidityChange += (_, e) =>
        {
            Console.WriteLine($"New humidity: {e.Humidity}");
        };
    }
};

socket_plug

using var miHome = new MiHome();

miHome.OnSocketPlug += (_, socketPlug) =>
{
    if (socketPlug.Sid == "158d00015dc6cc") // sid of specific device
    {
        Console.WriteLine(socketPlug); // sample output Status: on, Inuse: 1, Load Power: 2.91V, Power Consumed: 37049W, Voltage: 3.6V

        socketPlug.TurnOff();
        Task.Delay(5000).Wait();
        socketPlug.TurnOn();
    }
};

motion_sensor motion_sensor_2

using var miHome = new MiHome();

//miHome.OnAqaraMotionSensor += (_, motionSensor) =>
miHome.OnMotionSensor += (_, motionSensor) =>
{
    if (motionSensor.Sid == "158d00015dc6cc") // sid of specific device
    {
        Console.WriteLine(motionSensor); // sample output Status: motion, Voltage: 3.035V, NoMotion: 0s

        motionSensor.OnMotion += (_, __) =>
        {
            Console.WriteLine($"{DateTime.Now}: Motion detected !");
        };

        motionSensor.OnNoMotion += (_, e) =>
        {
            Console.WriteLine($"{DateTime.Now}: No motion for {e.Seconds}s !");
        };
    }
};

door_window_sensor aqara_door_window_sensor

using var miHome = new MiHome();

//miHome.OnAqaraOpenCloseSensor += (_, windowSensor) =>
miHome.OnDoorWindowSensor += (_, windowSensor) =>
{
    if (windowSensor.Sid == "158d00015dc6cc") // sid of specific device
    {
        Console.WriteLine(windowSensor); // sample output Status: close, Voltage: 3.025V

        windowSensor.OnOpen += (_, __) =>
        {
            Console.WriteLine($"{DateTime.Now}: Window opened !");
        };

        windowSensor.OnClose += (_, __) =>
        {
            Console.WriteLine($"{DateTime.Now}: Window closed !");
        };

    }
};

water_sensor

using var miHome = new MiHome();

miHome.OnWaterLeakSensor += (_, waterLeakSensor) =>
{
    if (waterLeakSensor.Sid == "158d00015dc6cc") // sid of specific device
    {
        Console.WriteLine(waterLeakSensor); // Status: no_leak, Voltage: 3.015V

        waterLeakSensor.OnLeak += (_, __) =>
        {
            Console.WriteLine("Water leak detected !");
        };

        waterLeakSensor.OnNoLeak += (_, __) =>
        {
            Console.WriteLine("NO leak detected !");
        };

    }
};

smoke_sensor

using var miHome = new MiHome();

miHome.OnSmokeSensor += (_, smokeSensor) =>
{
    if (smokeSensor.Sid == "158d00015dc6cc") // sid of specific device
    {
        Console.WriteLine(smokeSensor); // sample output Alarm: off, Density: 0, Voltage: 3.075V

        smokeSensor.OnAlarm += (_, __) =>
        {
            Console.WriteLine("Smoke detected !");
        };

        smokeSensor.OnAlarmStopped += (_, __) =>
        {
            Console.WriteLine("Smoke alarm stopped");
        };

        smokeSensor.OnDensityChange += (_, e) =>
        {
            Console.WriteLine($"Density changed {e.Density}");
        };
    }
};

wireless dual wall switch

using var miHome = new MiHome();

miHome.OnWirelessDualWallSwitch += (_, wirelessDualSwitch) =>
{
    if (wirelessDualSwitch.Sid == "158d00015dc6cc") // sid of specific device
    {
        Console.WriteLine(wirelessDualSwitch);

        wirelessDualSwitch.OnLeftClick += (_) =>
        {
            Console.WriteLine("Left button clicked !");
        };

        wirelessDualSwitch.OnRightDoubleClick += (_) =>
        {
            Console.WriteLine("Right button double clicked !");
        };

        wirelessDualSwitch.OnLeftLongClick += (_) =>
        {
            Console.WriteLine("Left button long clicked !");
        };

    }
};

aqara_cube_sensor

using var miHome = new MiHome();

miHome.OnAqaraCubeSensor += (_, aqaraQube) =>
{
    if (aqaraQube.Sid == "158d00015dc6cc") // sid of specific device
    {
        Console.WriteLine(aqaraQube);

        aqaraQube.OnStatusChanged += (sender, eventArgs) =>
        {
            Console.WriteLine($"{sender} | {eventArgs.Status}");
        };

    }
};

humidifier

Before using the library you need to know IP and TOKEN of your air humidifier. If you don't know these parameters try to use the following code in order to discover air humidifiers in your LAN

AirHumidifier.OnDiscovered += (_, humidifier) =>
{
    Console.WriteLine($"ip: {humidifier.Ip}, token: {humidifier.Token}");
    // sample output ip: 192.168.1.5, token: 4a3a2f017b70097a850558c35c953b55
};

AirHumidifier.DiscoverDevices();

If your device hides his token follow these instructions in order to extract it.

Basic scenario

var airHumidifier = new AirHumidifier("<ip here>", "<token here>");
Console.WriteLine(airHumidifier);
/* sample output
Power: on
Mode: high
Temperature: 32.6 °C
Humidity: 34%
LED brightness: bright
Buzzer: on
Child lock: off
Target humidity: 50%
Model: zhimi.humidifier.v1
IP Address:192.168.1.5
Token: 4a3a2f017b70097a850558c35c953b55
*/

Functions

var airHumidifier = new AirHumidifier("<ip here>", "<token here>");
airHumidifier.PowerOn(); // power on
airHumidifier.PowerOff(); // power off
airHumidifier.SetMode(AirHumidifier.Mode.High); // set fan mode high/medium/low
airHumidifier.GetTemperature(); // get temperature
airHumidifier.GetHumidity(); // get humidity
airHumidifier.SetBrightness(AirHumidifier.Brightness.Bright); // set brighness bright/dim/off
airHumidifier.BuzzerOn(); // set buzzer sound on
airHumidifier.BuzzerOff(); // set buzzer sound off
airHumidifier.ChildLockOn(); // set child lock on
airHumidifier.ChildLockOff(); // set child lock oаа
airHumidifier.GetTargetHumidity(); // get humidity limit 20/30/40/50/60/70/80 %

Async versions of the operations above also supported.

mirobot

Before using the library you need to know IP and TOKEN of your Mi Robot.

If you don't know these parameters try to use the following code in order to discover mi robots in your LAN

MiRobotV1.OnDiscovered += (_, e) =>
{
	Console.WriteLine($"{e.Ip}, {e.Serial}, {e.Type}, {e.Token}");
};

MiRobotV1.DiscoverDevices()

If your device hides his token (you get 'ffffffffffffffffffffffffffffffff' instead of token) follow these instructions in order to extract it.

Supported methods

var miRobot = new MiRobotV1("<ip here>", "<token here>");
miRobot.Start(); // start the clean up
miRobot.Stop(); // stop the clean up
miRobot.Pause(); // pause the clean up
miRobot.Spot(); // start spot clean up
miRobot.Home(); // go back to the base station
miRobot.FindMe(); // tell the robot to give a voice

Async versions of the operations above also supported.

Warning: Mi Robot stores client requests in memory and doesn't allow to send request with the same client id twice.

It means that if you run the code snippet bellow twice.

var miRobot = new MiRobotV1("<ip here>", "<token here>");
miRobot.Start(); // start the clean up

The second attempt will fail. Work around is to set client id manually (usually increasing to 1 works)

var miRobot = new MiRobotV1("<ip here>", "<token here>", 2); // client request id is set to 2
miRobot.Start(); // start the clean up