# react-native-ali-smartliving

[![npm version](http://img.shields.io/npm/v/react-native-ali-smartliving.svg?style=flat-square)](https://npmjs.org/package/react-native-ali-smartliving "View this project on npm")
[![npm downloads](http://img.shields.io/npm/dm/react-native-ali-smartliving.svg?style=flat-square)](https://npmjs.org/package/react-native-ali-smartliving "View this project on npm")
[![npm licence](http://img.shields.io/npm/l/react-native-ali-smartliving.svg?style=flat-square)](https://npmjs.org/package/react-native-ali-smartliving "View this project on npm")
[![Platform](https://img.shields.io/badge/platform-ios%20%7C%20android-989898.svg?style=flat-square)](https://npmjs.org/package/react-native-ali-smartliving "View this project on npm")

Component implementation for smartliving WiFi SDK of Ali fy(飞燕) platform [生活物联网平台](https://living.aliyun.com/), to control ali-smartliving-device-alios-things with direct([websocket](https://github.com/flyskywhy/g/blob/master/i%E4%B8%BB%E8%A7%82%E7%9A%84%E4%BD%93%E9%AA%8C%E6%96%B9%E5%BC%8F/t%E5%BF%AB%E4%B9%90%E7%9A%84%E4%BD%93%E9%AA%8C/%E7%94%B5%E4%BF%A1/Net/%E7%89%A9%E8%81%94%E7%BD%91/SmartLiving/AliSmartLiving%E4%BD%BF%E7%94%A8%E8%AF%A6%E8%A7%A3.md#%E8%AE%BE%E5%A4%87%E7%AB%AF%E6%B7%BB%E5%8A%A0-websocket-server-%E6%94%AF%E6%8C%81)) or indirect WiFi mode.

Suppor 蓝牙辅助配网 of [配置App配网引导方式](https://help.aliyun.com/document_detail/127494.htm).

## Install
```
npm install react-native-ali-smartliving
```

### iOS

    cd ios
    pod install

Copy `yw_1222_china_production.jpg` downloaded from `https://living.aliyun.com/` to `ios/`, then drag it into Xcode where `AppDelegate.m` lives. Ref to [集成安全图片](https://help.aliyun.com/document_detail/143857.html).

Drag `Pods | Development Pods | react-native-ali-smartliving | Resources | xib`  into top of Project in Xcode and select "Added folders: Create groups".

* In `ios/YOUR_PROJECT/AppDelegate.m`

```
#import <react-native-ali-smartliving/AliLiving.h>
...
[AliLiving InitializeSmartliving:application launchOptions:launchOptions];
```

You can customize and localize the login View, ref to the commit "feat(react-native-ali-smartliving): in iOS login View, add a new button to showEmailFindPasswordView, and hide the button which showFindPasswordView with Phone"

[Add WiFi support for ios 13 and above](https://github.com/react-native-netinfo/react-native-netinfo/issues/263#issuecomment-808727622).

Add `NSBluetoothAlwaysUsageDescription` and `NSBluetoothPeripheralUsageDescription` keys to your `Info.plist`.

### Android
Copy `yw_1222_china_production.jpg` downloaded from `https://living.aliyun.com/` to `android/app/src/main/res/drawable/` . Ref to [集成安全图片](https://help.aliyun.com/document_detail/143857.html), and if runtime `ErrorCode = 212` at `com.alibaba.wireless.security.open.SecException` after upgrade `com.aliyun.iot.aep.sdk:apiclient` in `android/dependency.gradle`, you need upload apk and download jpg again.

* In `android/app/build.gradle`

```
/**
 * Whether to use Aliyun encryption pic such as yw_1222_china_production.jpg.
 */
def useAliyunEncryptionPic = true

android {
    defaultConfig {
        minSdkVersion 18
        targetSdkVersion 26
        multiDexEnabled true
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a" // Aliyun livinglink SDK only support these two ABIs
        }
    }
    signingConfigs {
        debug {
            if (useAliyunEncryptionPic) {
                v2SigningEnabled false
                storeFile file(RELEASE_STORE_FILE)
                storePassword RELEASE_STORE_PASSWORD
                keyAlias RELEASE_KEY_ALIAS
                keyPassword RELEASE_KEY_PASSWORD
            } else {
                storeFile file('debug.keystore')
                storePassword 'android'
                keyAlias 'androiddebugkey'
                keyPassword 'android'
            }
        }
        release {
            storeFile file(RELEASE_STORE_FILE)
            storePassword RELEASE_STORE_PASSWORD
            keyAlias RELEASE_KEY_ALIAS
            keyPassword RELEASE_KEY_PASSWORD
        }
    }
}
```

* In `node_modules/react-native-ali-smartliving/android/dependency.gradle`

If node_modules/react-native-svg not exist in your project, you should use
```
api 'com.aliyun.iot.aep.page:rn:0.3.6.1'
```

* In `node_modules/react-native-ali-smartliving/android/build.gradle`

If RN >= 0.65 that remove `sourceCompatibility JavaVersion.VERSION_1_8` ref to [https://react-native-community.github.io/upgrade-helper/?from=0.64.3&to=0.65.0](https://react-native-community.github.io/upgrade-helper/?from=0.64.3&to=0.65.0), you should also remove
```
    compileOptions {
        sourceCompatibility rootProject.ext.javaSourceCompatibility
        targetCompatibility rootProject.ext.javaTargetCompatibility
    }
```

* In `MainApplication.java`

```
// import com.aliyun.iot.aep.sdk.framework.AApplication;

@Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
            new AliLivingReactPackage(getApplication()),
      );
    }
```

    find android/app/src/main/java/ -name MainApplication.java | xargs sed -i -e "s/extends Application/extends com.aliyun.iot.aep.sdk.framework.AApplication/"

* In `application` of `AndroidManifest.xml`

```
tools:replace="android:allowBackup"
android:usesCleartextTraffic="true"
```

`android:usesCleartextTraffic="true"` here is for WebSocket working in release version, ref to `https://github.com/facebook/react-native/issues/24361`

## Usage
```jsx
import React from 'react';
import { View } from 'react-native';
import meshModule from 'react-native-ali-smartliving';

export default class MeshModuleExample extends React.Component {
    constructor(props) {
        super(props);
        meshModule.passthroughMode = {
            silan: [
                'a17hN4W4777',  // PRODUCT_KEY
                'directWifi',   // the fake PRODUCT_KEY needed by direct WiFi mode device
            ],
            sllc: [
                'directWifi',
            ],
        };
    }

    componentDidMount() {
        meshModule.addListener('leScan', this.onLeScan);
        meshModule.doInit();
        meshModule.startScan({
            meshName: 'sysin_mesh', // if 'sysin_mesh', will scan unclaimed devices, otherwise not
            timeoutSeconds: 10,
        });
    }

    // after meshModule.startScan() found some device, will cause onLeScan() here
    onLeScan = data => console.warn(data)

    render() {
        return (
            <View/>
        );
    }
}
```

## APP method with different Device WiFi mode
The device can be in official indirect WiFi mode or [设备端添加 WebSocket Server 支持](https://github.com/flyskywhy/g/blob/master/i%E4%B8%BB%E8%A7%82%E7%9A%84%E4%BD%93%E9%AA%8C%E6%96%B9%E5%BC%8F/t%E5%BF%AB%E4%B9%90%E7%9A%84%E4%BD%93%E9%AA%8C/%E7%94%B5%E4%BF%A1/Net/%E7%89%A9%E8%81%94%E7%BD%91/SmartLiving/AliSmartLiving%E4%BD%BF%E7%94%A8%E8%AF%A6%E8%A7%A3.md#%E8%AE%BE%E5%A4%87%E7%AB%AF%E6%B7%BB%E5%8A%A0-websocket-server-%E6%94%AF%E6%8C%81) as unofficial direct WiFi mode.

### method with direct or indirect WiFi mode
#### targetAddress
Get the real address of the `data.params` comes from various `opcode` in `AliLiving.java`.

#### isOnline
Get the online status of the `data.params` comes from various `opcode` in `AliLiving.java`.

#### isOn
Get the power status of the `data.params` comes from various `opcode` in `AliLiving.java`.

#### changePower

#### changeScene

#### setAlarm
Note whether you defined TimezoneOffset described as code comment in `index.native.js`.

Note whether you use alarmId 0 or alarmId 1 as 1st LocalTimer described as code comment in `index.native.js`.

### method with direct WiFi mode
No need login, even no need burn info like productKey from `https://living.aliyun.com` into device, no need change mac address of device, you can still controll the device, only need your smartphone connect WiFi to the AP comes from the device.

##### socket device simulated on computer and controlled by APP
If you want just debug with a device simulated on computer, you need

    node node_modules/react-native-ali-smartliving/simSocketDevice/server.js

and comment the line `state.details.ipAddress.match` in `index.native.js`

and edit `let host = this.directWifiIpv4Byte3 + '1'` to e.g. `let host = '192.168.1.100'` where `192.168.1.100` is the IP address of you computer which connected the same LAN that your smartphone connected.

##### websocket device simulated on computer and controlled by Web
If you want just debug with a device simulated on computer, you need

    git clone https://github.com/warmcat/libwebsockets -b v2.4.1

add `lwsl_notice(" %s\n", (const char *)in);` in `case LWS_CALLBACK_RECEIVE:` of `libwebsockets/test-apps/test-server-dumb-increment.c`, then

    cd libwebsockets
    mkdir build
    cd build
    cmake ..
    make
    ./bin/libwebsockets-test-server --port=8821

and edit `let host = this.directWifiIpv4Byte3 + '1'` to e.g. `let host = '127.0.0.1'` and edit `local-ali-smartliving` to `dumb-increment-protocol` in `index.web.js`.

### method with indirect WiFi mode

#### login
Actually there is no login function supported by Ali ilop sdk, when it's not login state,
the login and register dialog will appears automatically.

Default only email register, if you also want mobile register, on Android, modify
`android/src/main/java/ugen/fy/plugin/OALoginActivity.java` supportMobile
to true, on iOS, customize and localize the login View described above, and you need buy sms support on `https://living.aliyun.com/`.

#### logout

#### startScan
example:
```
meshModule.addListener('leScan', console.warn);
meshModule.startScan({
        meshName: 'sysin_mesh', // if 'sysin_mesh', will scan unclaimed devices, otherwise not
        timeoutSeconds: 10,
});
```

#### configNode
when adding device, the param is:
```
{
    node {
        macAddress: 'A0:FB:42:00:A1:DB',    // mac of the device found from onLeScan after startScan
        type: {
            p: 'a17hN4W4777',               // PRODUCT_KEY
            d: 5, // 5 comes from onLeScan means DiscoveryType.COMBO_SUBTYPE_0X03_DEVICE, other discovery type not debug yet
        },
    },
    cfg: {
        newName: 'ssid of your WiFi',
        newPwd: 'password of your WiFi',
    },
    isToClaim: true,
}
```
when removing device, the param is:
```
{
    node {
        meshAddress: 'vvssa4DEzVglIGxDl777000000', // iotId
    },
    isToClaim: false,
}
```

#### loadNodes
load devices info from `https://living.aliyun.com` and convert them into our nodes format, then init them in native code and return nodes.

the param is:
```
{
    oldNodes = [{
        a: 'vvssa4DEzVglIGxDl777000000',    // iotId
        m: 'A0:FB:42:00:A1:DB',             // mac
//         i: {
//             v: 'app-1.6.0-20201201.1027'    // fwVersion
//         },
//         n: 'testDeviceName7',
//         t: {
//             d: 0, // 0 comes from loadNodes({forceRefresh: true}) means DiscoveryType.LOCAL_ONLINE_DEVICE
//             p: 'a17hN4W4777',               // PRODUCT_KEY
//         }
    }],
    forceRefresh = false,
}
```

#### getCurrentAccountMessage

#### send
example:
```
await meshModule.send(
    '/uc/listBindingByAccount',                 // path
    JSON.stringify({pageNo: 1, pageSize: 100}), // params
    '1.0.8',                                    // version
    true,                                       // iotAuth
)
```

#### getCurrentSsid

#### setAlarm

#### getAlarm

#### getFirmwareVersion

#### getOtaUpgradeableList

#### startOta

#### startAliSocketListener (Not test yet, waiting your result)
When `getAliSocketListenerState()` return not CONNECTED, you need `startAliSocketListener()` before `subscribe()`.

example:
```
await meshModule.startAliSocketListener();
```

#### stopAliSocketListener (Not test yet, waiting your result)
example:
```
await meshModule.stopAliSocketListener();
```

#### getAliSocketListenerState
Will return CONNECTED with login, otherwise, please use `startAliSocketListener()`.

#### subscribe (Work on Android, but not iOS for no CONNECTED, don't known why, PR is welcome)
Need `getAliSocketListenerState()` return CONNECTED.

example:
```
meshModule.addListener('subscribeState', console.warn); // subscribe successful or not
meshModule.addListener('subscribeDownstream', console.warn); // data comes from topic
await meshModule.subscribe(topic);
```

#### unsubscribe
Need `getAliSocketListenerState()` return CONNECTED.

#### ayncSendPublishRequest (Not test yet, waiting your result)
AKA publish.

Need `getAliSocketListenerState()` return CONNECTED.

#### asyncSendRequest (Not implement yet, waiting your PR)
AKA invokeWithTopic.

Need `getAliSocketListenerState()` return CONNECTED.

Ref to [长连接通道SDK](https://help.aliyun.com/document_detail/130114.html)

## References

* [生活物联网平台开发文档](https://help.aliyun.com/product/123207.html)
* [生活物联网平台开发指南](https://g.alicdn.com/aic/ilop-docs/2.2.1/index.html)
* [ilop android sdk api references](https://gaic.alicdn.com/ztms/ilop-ApiReference-V5/index.html)
* [ilop ios sdk api references](https://gaic.alicdn.com/ztms/ilop-ios-sdk-api-references-v6/html/index.html)
* [BoneEngine使用详解](https://github.com/flyskywhy/g/blob/master/i%E4%B8%BB%E8%A7%82%E7%9A%84%E4%BD%93%E9%AA%8C%E6%96%B9%E5%BC%8F/t%E5%BF%AB%E4%B9%90%E7%9A%84%E4%BD%93%E9%AA%8C/%E7%94%B5%E4%BF%A1/Os/AliOsThings/BoneEngine%E4%BD%BF%E7%94%A8%E8%AF%A6%E8%A7%A3.md)
* [AliSmartLiving使用详解](https://github.com/flyskywhy/g/blob/master/i%E4%B8%BB%E8%A7%82%E7%9A%84%E4%BD%93%E9%AA%8C%E6%96%B9%E5%BC%8F/t%E5%BF%AB%E4%B9%90%E7%9A%84%E4%BD%93%E9%AA%8C/%E7%94%B5%E4%BF%A1/Net/%E7%89%A9%E8%81%94%E7%BD%91/SmartLiving/AliSmartLiving%E4%BD%BF%E7%94%A8%E8%AF%A6%E8%A7%A3.md)
