Skip to content

Commit f56f271

Browse files
committed
UPDATE add example
1 parent 4eab221 commit f56f271

File tree

2 files changed

+192
-171
lines changed

2 files changed

+192
-171
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,27 @@ With this package you can control Xiaomi Mi Home devices that implement the miIO
33
Mi Robot Vacuum and other Smart Home devices.
44
These devices are commonly part of what Xiaomi calls the Mi Ecosystem which is branded as MiJia.
55

6+
# Example
7+
Get device info, status and start cleaning of mi robot vacuum
8+
9+
$device = new Device();
10+
11+
$device
12+
->setDeviceName('mirobot_vacuum')
13+
->setToken('00112233445566778899aabbccddeeff');
14+
15+
$miio = new MiIO();
16+
17+
$deviceInfoResult = $miio->getInfo($device);
18+
19+
$statusResult = $miio->send($device, 'get_status');
20+
21+
$miio->send($device, 'app_start'); // start cleaning
22+
23+
24+
More information about the protocol and commands can be found at
25+
https://github.com/marcelrv/XiaomiRobotVacuumProtocol
26+
627
## License
728

829
This project is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE - see the [LICENSE.md](/LICENSE.md) file for details.

src/MiIO.php

Lines changed: 171 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -13,175 +13,175 @@
1313
*/
1414
class MiIO
1515
{
16-
const CACHE_KEY = 'MiIO';
17-
18-
const PORT = 54321;
19-
20-
const TIMEOUT = 5;
21-
22-
const INFO = 'miIO.info';
23-
24-
/**
25-
* @param Device $device
26-
*/
27-
public function getInfo(Device $device)
28-
{
29-
$response = $this->send($device, static::INFO);
30-
}
31-
32-
/**
33-
* @param Device $device
34-
* @return Device|null
35-
*/
36-
private function init(Device &$device)
37-
{
38-
if (strlen($device->getIpAddress())) {
39-
/** @var Packet $packet */
40-
$packet = app(Packet::class);
41-
$helo = $packet->getHelo();
42-
43-
$start = time();
44-
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
45-
46-
while (time() < ($start + 10)) {
47-
$response = $this->getSocketResponse($device->getIpAddress(), $socket, $helo);
48-
49-
if (!empty($response)) {
50-
$response = bin2hex($response);
51-
}
52-
53-
if (!empty($response)) {
54-
$packet = new Packet($response);
55-
if ($packet->getDeviceType()
56-
&& $packet->getSerial()) {
57-
$device->setDeviceType($packet->getDeviceType());
58-
$device->setSerial($packet->getSerial());
59-
$device->setTimeDelta(hexdec($packet->getTimestamp()) - time());
60-
socket_close($socket);
61-
62-
return $device;
63-
}
64-
}
65-
}
66-
if ($socket) {
67-
socket_close($socket);
68-
}
69-
}
70-
71-
return null;
72-
}
73-
74-
/**
75-
* @param Device $device
76-
* @param string $command
77-
* @param array $params
78-
* @return array
79-
*/
80-
public function send(Device $device, $command, $params = [])
81-
{
82-
if (!$device->isInitialized()) {
83-
$this->init($device);
84-
}
85-
86-
if (!$device->isInitialized()) {
87-
return [];
88-
}
89-
90-
$request = new Request();
91-
$request
92-
->setMethod($command)
93-
->setParams($params);
94-
95-
return $this->getResponse($device, $request);
96-
}
97-
98-
/**
99-
* @param Device $device
100-
* @param Request $request
101-
* @return array
102-
*/
103-
public function getResponse(Device $device, Request $request)
104-
{
105-
$cacheKey = static::CACHE_KEY . $device->getIpAddress();
106-
$requestId = (int)\Cache::get($cacheKey);
107-
\Cache::forever($cacheKey, $requestId + 1);
108-
109-
$request->setId($requestId);
110-
111-
$data = $device->encrypt($request);
112-
113-
$packet = new Packet();
114-
$packet
115-
->setData($data)
116-
->setDevice($device);
117-
118-
$start = time();
119-
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
120-
121-
while (time() < ($start + 10)) {
122-
$packet->setTimestamp($device->getTimeDelta() + time());
123-
$response = $this->getSocketResponse($device->getIpAddress(), $socket, (string)$packet);
124-
125-
if (!empty($response)) {
126-
$response = bin2hex($response);
127-
}
128-
129-
$result = $this->decrypt($device, $response);
130-
131-
if (strlen($result)) {
132-
$response = json_decode(preg_replace('/[\x00-\x1F\x7F]/', '', $result), true);
133-
134-
if (!empty($response['id']) && $response['id'] === $requestId) {
135-
socket_close($socket);
136-
137-
return $response;
138-
}
139-
}
140-
}
141-
142-
return [];
143-
}
144-
145-
/**
146-
* @param string $ip
147-
* @param \Resource $socket
148-
* @param string $data
149-
* @return string|null
150-
*/
151-
private function getSocketResponse($ip, $socket, $data)
152-
{
153-
if (ctype_xdigit($data)) {
154-
$data = hex2bin($data);
155-
}
156-
157-
$buf = null;
158-
159-
try {
160-
socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1);
161-
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 5, 'usec' => 0]);
162-
163-
socket_sendto($socket, $data, strlen($data), 0, $ip, static::PORT);
164-
165-
socket_recvfrom($socket, $buf, 1024, 0, $name, $port);
166-
} catch (\Throwable $e) {
167-
}
168-
169-
return $buf;
170-
}
171-
172-
/**
173-
* @param Device $device
174-
* @param string $response
175-
* @return string
176-
*/
177-
private function decrypt(Device $device, $response)
178-
{
179-
if (!empty($response)) {
180-
$packet = new Packet($response);
181-
182-
return $device->decrypt($packet->getData());
183-
}
184-
185-
return '';
186-
}
16+
const CACHE_KEY = 'MiIO';
17+
18+
const PORT = 54321;
19+
20+
const TIMEOUT = 5;
21+
22+
const INFO = 'miIO.info';
23+
24+
/**
25+
* @param Device $device
26+
* @return array
27+
*/
28+
public function getInfo(Device $device)
29+
{
30+
return $this->send($device, static::INFO);
31+
}
32+
33+
/**
34+
* @param Device $device
35+
* @return Device|null
36+
*/
37+
private function init(Device &$device)
38+
{
39+
if (strlen($device->getIpAddress())) {
40+
$packet = new Packet();
41+
$helo = $packet->getHelo();
42+
43+
$start = time();
44+
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
45+
46+
while (time() < ($start + 10)) {
47+
$response = $this->getSocketResponse($device->getIpAddress(), $socket, $helo);
48+
49+
if (!empty($response)) {
50+
$response = bin2hex($response);
51+
}
52+
53+
if (!empty($response)) {
54+
$packet = new Packet($response);
55+
if ($packet->getDeviceType()
56+
&& $packet->getSerial()) {
57+
$device->setDeviceType($packet->getDeviceType());
58+
$device->setSerial($packet->getSerial());
59+
$device->setTimeDelta(hexdec($packet->getTimestamp()) - time());
60+
socket_close($socket);
61+
62+
return $device;
63+
}
64+
}
65+
}
66+
if ($socket) {
67+
socket_close($socket);
68+
}
69+
}
70+
71+
return null;
72+
}
73+
74+
/**
75+
* @param Device $device
76+
* @param string $command
77+
* @param array $params
78+
* @return array
79+
*/
80+
public function send(Device $device, $command, $params = [])
81+
{
82+
if (!$device->isInitialized()) {
83+
$this->init($device);
84+
}
85+
86+
if (!$device->isInitialized()) {
87+
return [];
88+
}
89+
90+
$request = new Request();
91+
$request
92+
->setMethod($command)
93+
->setParams($params);
94+
95+
return $this->getResponse($device, $request);
96+
}
97+
98+
/**
99+
* @param Device $device
100+
* @param Request $request
101+
* @return array
102+
*/
103+
public function getResponse(Device $device, Request $request)
104+
{
105+
$cacheKey = static::CACHE_KEY . $device->getIpAddress();
106+
$requestId = (int)\Cache::get($cacheKey);
107+
\Cache::forever($cacheKey, $requestId + 1);
108+
109+
$request->setId($requestId);
110+
111+
$data = $device->encrypt($request);
112+
113+
$packet = new Packet();
114+
$packet
115+
->setData($data)
116+
->setDevice($device);
117+
118+
$start = time();
119+
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
120+
121+
while (time() < ($start + 10)) {
122+
$packet->setTimestamp($device->getTimeDelta() + time());
123+
$response = $this->getSocketResponse($device->getIpAddress(), $socket, (string)$packet);
124+
125+
if (!empty($response)) {
126+
$response = bin2hex($response);
127+
}
128+
129+
$result = $this->decrypt($device, $response);
130+
131+
if (strlen($result)) {
132+
$response = json_decode(preg_replace('/[\x00-\x1F\x7F]/', '', $result), true);
133+
134+
if (!empty($response['id']) && $response['id'] === $requestId) {
135+
socket_close($socket);
136+
137+
return $response;
138+
}
139+
}
140+
}
141+
142+
return [];
143+
}
144+
145+
/**
146+
* @param string $ip
147+
* @param \Resource $socket
148+
* @param string $data
149+
* @return string|null
150+
*/
151+
private function getSocketResponse($ip, $socket, $data)
152+
{
153+
if (ctype_xdigit($data)) {
154+
$data = hex2bin($data);
155+
}
156+
157+
$buf = null;
158+
159+
try {
160+
socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1);
161+
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 5, 'usec' => 0]);
162+
163+
socket_sendto($socket, $data, strlen($data), 0, $ip, static::PORT);
164+
165+
socket_recvfrom($socket, $buf, 1024, 0, $name, $port);
166+
} catch (\Throwable $e) {
167+
}
168+
169+
return $buf;
170+
}
171+
172+
/**
173+
* @param Device $device
174+
* @param string $response
175+
* @return string
176+
*/
177+
private function decrypt(Device $device, $response)
178+
{
179+
if (!empty($response)) {
180+
$packet = new Packet($response);
181+
182+
return $device->decrypt($packet->getData());
183+
}
184+
185+
return '';
186+
}
187187
}

0 commit comments

Comments
 (0)