Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix for malformed json from airpurifier m1 #199

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
09d8799
Add new ca1 humidifier
jnchman Mar 27, 2018
d6a7df3
Add activateZoneClean
jnchman Mar 27, 2018
6289ca3
Add refreshDelay
jnchman Apr 2, 2018
4b9cc77
colorTemperatureRange is function returns promise
winguse Nov 18, 2018
783c544
Update yeelight.js
winguse Nov 18, 2018
b82fa53
Bugfix for malformed json from airpurifier m1
Dec 8, 2018
fd87c9a
Adding support for Philips ZhiRui Downlight
Vanwards Jan 19, 2019
03e205b
Added model check to avoid calling miio.info
Algram Jan 27, 2019
57870fd
magnet value fix
Johnsel Jan 28, 2019
6b3f530
Adding support for Philips ZhiRui Downlight
Vanwards Feb 23, 2019
1585519
Add promise resolve for faster responses
Algram Mar 4, 2019
7696042
Fix Philips Eyecare Smart Lamp 2 support and add comments to props ac…
almsh Jun 29, 2019
f4e28cd
Add brightness range coercion
almsh Jun 29, 2019
9372cfe
Add Instructions for "Get Device Token Without Resetting on Non-Jailb…
mediter Jul 11, 2019
e630b5a
minor updates:
mediter Jul 11, 2019
4132dd2
WIP: add device MiFan
mediter Jul 27, 2019
f092c82
Update models.js
loki29pl Nov 22, 2020
04e6254
Merge pull request #1 from loki29pl/patch-1
mediter Dec 5, 2020
ba9f360
implements https://github.com/aholstenson/miio/pull/297
Dec 29, 2020
99b082d
Merge pull request #1 from mediter/master
krim404 Dec 29, 2020
1f9579d
merged pending pull requests by hand
Dec 29, 2020
7adb5a1
added more models
Dec 29, 2020
9c59ef1
Merge pull request #2 from almsh/master
krim404 Dec 29, 2020
34b5357
Merge pull request #3 from Johnsel/magnet-fix
krim404 Dec 29, 2020
9de7f65
Merge pull request #4 from Algram/master
krim404 Dec 29, 2020
275bf8d
Merge pull request #5 from Vanwards/master
krim404 Dec 29, 2020
b2cfa2a
Merge pull request #6 from winguse/patch-1
krim404 Dec 29, 2020
bc5373a
Merge pull request #7 from Chao-Man/master
krim404 Dec 29, 2020
36235dd
merged all valid looking pending pull requests and added a few additi…
Dec 29, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
npm-debug.log
.DS_Store
173 changes: 173 additions & 0 deletions docs/devices/mifan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Development Notes for XiaoMi Fan

## Variants

1. [米家直流变频落地扇](https://item.mi.com/1182100014.html)
2. [米家直流变频落地扇 1x](https://www.mi.com/airfan1x/)
3. [智米直流变频落地扇 2 有线版](https://www.xiaomiyoupin.com/detail?gid=105764)
4. [智米直流变频落地扇 2s 无线版](https://www.xiaomiyoupin.com/detail?gid=105764)

## Feature Comparison

.| 米家直流变频落地扇 | 米家直流变频落地扇 1x | 智米直流变频落地扇 2 有线版 | 智米直流变频落地扇 2s 无线版
-|-|-|-|-|
Model|MiDCVariableFrequencyFan | DmakerFan | ZhiMiNaturalWindFan | ZhiMiDCVariableFrequencyFan
Model No. | ZLBPLDS012ZM | BPLDS01DM | a| b|
Price|299RMB|279RMB|399RMB|599RMB
Nature Wind|:white_check_mark:|:white_check_mark:|:white_check_mark:|:white_check_mark:|
Max Oscillation Range| 120º | 140º |120º |120º |
Min Noise Level| 33 dB(A)| 26.6 dB(A) | 28.6 dB(A) |28.6 dB(A) |
Fan Speed| 100 档|100 档|100 档|100 档|
Battery | :x: |:x: |:x: | 33.6 Wh Li-ion Battery
Weight | 3 kg| 2.8 kg| 3.2 kg | 3.5 kg
Dimension | 340×330×960mm | 343×330×950mm | 340×330×960mm | 340×330×960mm |

## Basic Configuration

Property | Allowed Values |
-|-|
Power | on / off |
WindMode | nature / normal |
Fan Speed | [1-100]%
Oscillation | on / off |
Oscillation Range | 30º, 60º, 90º, 120º (allow customization of upper-limit) |
Schedule Power Off | [1-8] measured in hours |
Indicator Light | on / off |
Buzzer | on / off |
Child lock | on / off |

> WindMode, Oscillation, Schedule Power Off, Child Lock require new capabilities, currently not defined.

## Special Configuration

- each model define a `static type() => model`
- for DmakerFan, change upper limit of Oscillation Range to 140º
- for ZhiMiDCVariableFrequencyFan, add Battery Level property

## Questions

- When do we need to define api?
- two main kinds of function to define in our type definition:

1. Ones that deal with in memory representation of the device
2. Ones that actually interacts with the device
3. use `propertyUpdated(key, value)` to update the in momery representation
4. define custome methods to interact with the device

- `miio` adopted `abstract-things` as basis beginning with version 15.0

you can define your custome type by using the following syntax:

```javascript
// import the capabilities you need
const Buzzer = require('./capabilities/buzzer');

// then
module.exports = Thing.type(Parent => class MiFan extends Parent.with(Buzzer){
static type() {
return 'miio:mifan';
}

constructor(options) {
super(options);

this.defineProperty('power', {
name: 'power',
mapper: v => v === 'on'
});
}
});
```

> - `Thing` is the base type, use `with()` to combine >= 1 functionalities on top of this base type
> - Anything defined using syntax similar to the one shown above can be used inside `with()`. That means they can be used to extend functionalities to form new Thing
> - `property.mapper` is used to convert input to an accepted value by the actual device

- In order to use the public API declared in `lib/device.js`, you need to do the following:

```javascript
const MiioApi = require('miio/device');
```

then wherever you want to communicate with the physical device, do the following

```js
this.MiioApi.call('get_prop', ["all"], {
refresh: ["all"]
});
```

based on `call(method, args, options)` in `lib/device.js`

`options.refresh` is used to read the property from the physical device after the change

this function returns a Promise, the following code snippet from `yeelight.js` demonstrate how to use this.

```js
changePower(power) {
// TODO: Support for duration
return this.call(
'set_power',
Yeelight.withEffect(power ? 'on' : 'off'),
{
refresh: [ 'power']
}
).then(MiioApi.checkOk);
}
```

## Methods Available for MiFan

Method | Description | Parameters | Result |
-| -| -|-|
get_prop | read the specified properties |["all"] | [true, "normal", 70, true, 60, 0, false, true, false, 3]
s_power | turn on / off the device | true / false | "ok"
s_mode | change windMode | "nature" / "normal" | "ok"
s_speed | change fanSpeed | an Int in [1-100] | "ok"
s_roll | whether or not the head should swing | true / false | "ok"
s_angle | the range of head swing | an Int in [30, 60, 90, 120] / [30, 60, 90, 120, 140] | "ok"
s_t_off | setting scheduled power off | [0, 60, 120,180, 240, 300, 360, 420, 480] as in 60 * [0-8] | "ok"
s_light | whether the LED indicator lights are turned on | true / false | "ok"
s_sound | whether the device should buzz when changing a setting | true / false | "ok"
s_lock | whether the device can be controlled using the physical button | true / false | "ok"

## Result from `get_prop '[["all"]]'`

After updating the token for the device, try

`miio protocol call 10.0.1.13 get_prop '[["all"]]'`

result is a simple array with no label for each value

```json
[
false,
"normal",
55,
true,
140,
0,
false,
true,
false,
2
]
```

After some testing, we can determine what each value means, except the last one.

```json
{
"power": false,
"wind_mode": "normal",
"fan_speed": 55,
"roll": true,
"roll_angle": 140,
"schedule_off": 0,
"indicator": false,
"buzzer": true,
"child_lock": false
"?": 2
}
```

Binary file added docs/images/hexyl-output.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/ibackuptool-list.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/mihome-tokens.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions docs/ios-token-without-reset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Get Device Token Without Resetting on Non-Jailbroken iOS Devices

If you have devices already setup, their tokens could be hidden by default. In that case, if you want to get their tokens without setting them up one by one again, this document would show you the way. Because all the tokens are stored in a file named "######_mihome.sqlite"

## Step 1: Create an iOS Unencrypted Backup

## Step 2: Extract MiHome database

1. Install [iPhoneBackupTools](https://github.com/richinfante/iphonebackuptools) by [Rich Infante](https://github.com/richinfante)

```bash
npm i ibackuptool --save
```

2. Find the specific backup, we just need the udid

```bash
ibackuptool -l
```

the output would be something like this:

![ibackuptool list output](images/ibackuptool-list.jpg)

Specifically, we want the udid of the backup.

3. Extract the file "######_mihome.sqlite"

```bash
ibackuptool -b udid_from_step_3 -r backup.files --extract extraction_destination --filter "mihome.sqlite"
```

4. Locate the extracted file

```bash
cd extraction_destination/AppDomain-com.xiaomi.mihome/Documents
ls | grep mihome
```

## Step 3: Extract Tokens from MiHome database

1. Open the file in sqlite

```bash
sqlite3 77861590_mihome.sqlite
```

2. Adjust the options in sqlite3. These options are not necessary, but they do make the final output easier to read.

```bash
sqlite> .headers on
sqlite> .mode column
```

>"sqlite>" is the prompt in sqlite3, you only need to type what is after that

3. Extract the encrypted tokens

```bash
sqlite> select ZNAME, ZTOKEN, ZMAC, ZLOCALIP from ZDevice;
```

![mihome encrypted tokens](images/mihome-tokens.jpg)

## Step 4: Decrypt Tokens

> Ref [Obtain MiHome Device Token](https://github.com/jghaanstra/com.xiaomi-miio/blob/master/docs/obtain_token.md)

1. The latest Mi Home app store the tokens encrypted into a 96 character key and require an extra step to decode this into the actual token. Visit [this](http://aes.online-domain-tools.com/) website and enter the details as shown below:
** __Input type:__ text
* __Input text (hex):__ your 96 character key
* __Selectbox Plaintext / Hex:__ Hex
* __Function:__ AES
* __Mode:__ ECB
* __Key (hex):__ 00000000000000000000000000000000
* __Selectbox Plaintext / Hex:__ Hex

2. Hit the decrypt button. Your token are the first two lines of the right block of code. These two lines should contain a token of 32 characters and should be the correct token for your device.

Alternatively, for step 2 of step 4, you can download the decrypted token (filename __odt-IV-00000000000000000000000000000000.dat__), then use a tool called [hexyl](https://github.com/sharkdp/hexyl), it can output the decrypted token in text, the website only displays it as an image, the font it used for the image output is a bit hard on the eye.

After you install **hexyl**, run the following command, then you can copen the decrypted token as text. No squinting required.

```bash
hexyl path-to-downloaded-file
```

the output would be like this

![hexyl output](images/hexyl-output.jpg)

the token (32 characters) is the part enclosed in the yellow circle
4 changes: 4 additions & 0 deletions docs/management.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ reset and the follow the section _Configuring a new device_ found above.
6. Reconnect to your regular wireless network.
7. Readd the device in the Mi Home app so that it has access to your regular wireless network.

### Getting the token by extracting MiHome database

Please refer to [ios-token-without-reset.md](ios-token-without-reset.md)

## Setting the token of a device

If you want to update the token of a device use the `tokens update` command:
Expand Down
Binary file added lib/.DS_Store
Binary file not shown.
5 changes: 5 additions & 0 deletions lib/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ const DeviceManagement = require('./management');

const IDENTITY_MAPPER = v => v;

// Thing.type(class, func) => ?
module.exports = Thing.type(Parent => class extends Parent.with(Polling) {
static get type() {
return 'miio';
}

/**
* @static availableAPI
* @param {*} builder: where does builder come from?
*/
static availableAPI(builder) {
builder.action('miioModel')
.description('Get the model identifier of this device')
Expand Down
Loading