├── manage ├── easyui │ └── themes │ │ ├── default │ │ ├── numberbox.css │ │ ├── images │ │ │ ├── blank.gif │ │ │ ├── loading.gif │ │ │ ├── tabs_icons.png │ │ │ ├── tree_icons.png │ │ │ ├── combo_arrow.png │ │ │ ├── datebox_arrow.png │ │ │ ├── layout_arrows.png │ │ │ ├── linkbutton_bg.png │ │ │ ├── menu_arrows.png │ │ │ ├── panel_tools.png │ │ │ ├── slider_handle.png │ │ │ ├── tagbox_icons.png │ │ │ ├── calendar_arrows.png │ │ │ ├── datagrid_icons.png │ │ │ ├── messager_icons.png │ │ │ ├── spinner_arrows.png │ │ │ ├── accordion_arrows.png │ │ │ ├── pagination_icons.png │ │ │ ├── passwordbox_close.png │ │ │ ├── passwordbox_open.png │ │ │ ├── searchbox_button.png │ │ │ └── validatebox_warning.png │ │ ├── passwordbox.css │ │ ├── validatebox.css │ │ ├── splitbutton.css │ │ ├── filebox.css │ │ ├── radiobutton.css │ │ ├── checkbox.css │ │ ├── combo.css │ │ ├── datebox.css │ │ ├── progressbar.css │ │ ├── propertygrid.css │ │ ├── combobox.css │ │ ├── tagbox.css │ │ ├── messager.css │ │ ├── dialog.css │ │ ├── searchbox.css │ │ ├── sidemenu.css │ │ ├── pagination.css │ │ ├── switchbutton.css │ │ ├── slider.css │ │ ├── datalist.css │ │ ├── menubutton.css │ │ ├── tooltip.css │ │ ├── accordion.css │ │ ├── menu.css │ │ ├── spinner.css │ │ ├── textbox.css │ │ ├── layout.css │ │ ├── tree.css │ │ ├── calendar.css │ │ ├── window.css │ │ ├── linkbutton.css │ │ ├── panel.css │ │ └── datagrid.css │ │ ├── icons │ │ ├── no.png │ │ ├── ok.png │ │ ├── reload.png │ │ ├── search.png │ │ ├── edit_add.png │ │ └── edit_remove.png │ │ └── icon.css ├── images │ ├── icons │ │ ├── device.png │ │ ├── accessory.png │ │ ├── gateway.png │ │ └── homekit.png │ └── deviceIcons │ │ ├── Lock.png │ │ ├── Button.png │ │ ├── Button2.png │ │ ├── Button3.png │ │ ├── Gateway.png │ │ ├── PlugBase.png │ │ ├── AcPartner.png │ │ ├── PlugBase86.png │ │ ├── Vibration.png │ │ ├── ContactSensor.png │ │ ├── DuplexSwitch.png │ │ ├── MagicSquare.png │ │ ├── MotionSensor.png │ │ ├── MotionSensor2.png │ │ ├── SingleSwitch.png │ │ ├── SmokeDetector.png │ │ ├── WaterDetector.png │ │ ├── ContactSensor2.png │ │ ├── DuplexButton86.png │ │ ├── DuplexButton862.png │ │ ├── DuplexSwitchLN.png │ │ ├── ElectricCurtain.png │ │ ├── NatgasDetector.png │ │ ├── SingleButton86.png │ │ ├── SingleSwitchLN.png │ │ ├── TemperatureAndHumiditySensor.png │ │ └── TemperatureAndHumiditySensor2.png ├── login │ └── login.html └── css │ ├── icon.css │ └── device_icon.css ├── images ├── Lock.jpg ├── Button.jpg ├── Button2.jpg ├── Button3.jpg ├── Gateway.jpg ├── AcPartner.jpg ├── PlugBase.jpg ├── Vibration.jpg ├── syncValue.png ├── DuplexSwitch.jpg ├── MagicSquare.jpg ├── MotionSensor.jpg ├── PlugBase86.jpg ├── SingleSwitch.jpg ├── ContactSensor.jpg ├── ContactSensor2.jpg ├── Donate-AliPay.jpg ├── Donate-WeChat.jpg ├── DuplexButton86.jpg ├── DuplexSwitchLN.jpg ├── MotionSensor2.jpg ├── NatgasDetector.jpg ├── SingleButton86.jpg ├── SingleSwitchLN.jpg ├── SmokeDetector.jpg ├── WaterDetector.jpg ├── httpWebManage.png ├── DuplexButton862.jpg ├── ElectricCurtain.jpg ├── ElectricCurtainBattery.jpg ├── TemperatureAndHumiditySensor.jpg └── TemperatureAndHumiditySensor2.jpg ├── lib ├── AccessoryUtil.js ├── GatewayUtil.js ├── LogUtil.js ├── DeviceUtil.js ├── ParseUtil.js └── ConfigUtil.js ├── package.json └── parser ├── DeviceParser.js ├── SwitchVirtualBasePressParser.js ├── SmokeDetectorParser.js ├── NatgasDetectorParser.js ├── MotionSensorParser.js ├── ContactSensorParser.js ├── ContactSensor2Parser.js ├── WaterDetectorParser.js ├── PlugBaseParser.js ├── PlugBase86Parser.js ├── ElectricCurtainParser.js ├── SingleSwitchParser.js ├── SingleSwitchLNParser.js ├── VibrationSensorParser.js ├── ElectricCurtainBatteryParser.js ├── LockParser.js ├── DuplexSwitchParser.js └── DuplexSwitchLNParser.js /manage/easyui/themes/default/numberbox.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/Lock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Lock.jpg -------------------------------------------------------------------------------- /images/Button.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Button.jpg -------------------------------------------------------------------------------- /images/Button2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Button2.jpg -------------------------------------------------------------------------------- /images/Button3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Button3.jpg -------------------------------------------------------------------------------- /images/Gateway.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Gateway.jpg -------------------------------------------------------------------------------- /images/AcPartner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/AcPartner.jpg -------------------------------------------------------------------------------- /images/PlugBase.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/PlugBase.jpg -------------------------------------------------------------------------------- /images/Vibration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Vibration.jpg -------------------------------------------------------------------------------- /images/syncValue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/syncValue.png -------------------------------------------------------------------------------- /images/DuplexSwitch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/DuplexSwitch.jpg -------------------------------------------------------------------------------- /images/MagicSquare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/MagicSquare.jpg -------------------------------------------------------------------------------- /images/MotionSensor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/MotionSensor.jpg -------------------------------------------------------------------------------- /images/PlugBase86.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/PlugBase86.jpg -------------------------------------------------------------------------------- /images/SingleSwitch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/SingleSwitch.jpg -------------------------------------------------------------------------------- /images/ContactSensor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/ContactSensor.jpg -------------------------------------------------------------------------------- /images/ContactSensor2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/ContactSensor2.jpg -------------------------------------------------------------------------------- /images/Donate-AliPay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Donate-AliPay.jpg -------------------------------------------------------------------------------- /images/Donate-WeChat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/Donate-WeChat.jpg -------------------------------------------------------------------------------- /images/DuplexButton86.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/DuplexButton86.jpg -------------------------------------------------------------------------------- /images/DuplexSwitchLN.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/DuplexSwitchLN.jpg -------------------------------------------------------------------------------- /images/MotionSensor2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/MotionSensor2.jpg -------------------------------------------------------------------------------- /images/NatgasDetector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/NatgasDetector.jpg -------------------------------------------------------------------------------- /images/SingleButton86.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/SingleButton86.jpg -------------------------------------------------------------------------------- /images/SingleSwitchLN.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/SingleSwitchLN.jpg -------------------------------------------------------------------------------- /images/SmokeDetector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/SmokeDetector.jpg -------------------------------------------------------------------------------- /images/WaterDetector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/WaterDetector.jpg -------------------------------------------------------------------------------- /images/httpWebManage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/httpWebManage.png -------------------------------------------------------------------------------- /images/DuplexButton862.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/DuplexButton862.jpg -------------------------------------------------------------------------------- /images/ElectricCurtain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/ElectricCurtain.jpg -------------------------------------------------------------------------------- /manage/images/icons/device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/icons/device.png -------------------------------------------------------------------------------- /images/ElectricCurtainBattery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/ElectricCurtainBattery.jpg -------------------------------------------------------------------------------- /manage/easyui/themes/icons/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/icons/no.png -------------------------------------------------------------------------------- /manage/easyui/themes/icons/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/icons/ok.png -------------------------------------------------------------------------------- /manage/images/icons/accessory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/icons/accessory.png -------------------------------------------------------------------------------- /manage/images/icons/gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/icons/gateway.png -------------------------------------------------------------------------------- /manage/images/icons/homekit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/icons/homekit.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/Lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/Lock.png -------------------------------------------------------------------------------- /manage/easyui/themes/icons/reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/icons/reload.png -------------------------------------------------------------------------------- /manage/easyui/themes/icons/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/icons/search.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/Button.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/Button2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/Button2.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/Button3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/Button3.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/Gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/Gateway.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/PlugBase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/PlugBase.png -------------------------------------------------------------------------------- /images/TemperatureAndHumiditySensor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/TemperatureAndHumiditySensor.jpg -------------------------------------------------------------------------------- /images/TemperatureAndHumiditySensor2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/images/TemperatureAndHumiditySensor2.jpg -------------------------------------------------------------------------------- /manage/easyui/themes/icons/edit_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/icons/edit_add.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/AcPartner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/AcPartner.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/PlugBase86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/PlugBase86.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/Vibration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/Vibration.png -------------------------------------------------------------------------------- /manage/easyui/themes/icons/edit_remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/icons/edit_remove.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/ContactSensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/ContactSensor.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/DuplexSwitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/DuplexSwitch.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/MagicSquare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/MagicSquare.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/MotionSensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/MotionSensor.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/MotionSensor2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/MotionSensor2.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/SingleSwitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/SingleSwitch.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/SmokeDetector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/SmokeDetector.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/WaterDetector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/WaterDetector.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/blank.gif -------------------------------------------------------------------------------- /manage/images/deviceIcons/ContactSensor2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/ContactSensor2.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/DuplexButton86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/DuplexButton86.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/DuplexButton862.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/DuplexButton862.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/DuplexSwitchLN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/DuplexSwitchLN.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/ElectricCurtain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/ElectricCurtain.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/NatgasDetector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/NatgasDetector.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/SingleButton86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/SingleButton86.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/SingleSwitchLN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/SingleSwitchLN.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/loading.gif -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/tabs_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/tabs_icons.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/tree_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/tree_icons.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/combo_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/combo_arrow.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/datebox_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/datebox_arrow.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/layout_arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/layout_arrows.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/linkbutton_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/linkbutton_bg.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/menu_arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/menu_arrows.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/panel_tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/panel_tools.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/slider_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/slider_handle.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/tagbox_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/tagbox_icons.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/calendar_arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/calendar_arrows.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/datagrid_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/datagrid_icons.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/messager_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/messager_icons.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/spinner_arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/spinner_arrows.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/accordion_arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/accordion_arrows.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/pagination_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/pagination_icons.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/passwordbox_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/passwordbox_close.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/passwordbox_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/passwordbox_open.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/searchbox_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/searchbox_button.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/TemperatureAndHumiditySensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/TemperatureAndHumiditySensor.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/images/validatebox_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/easyui/themes/default/images/validatebox_warning.png -------------------------------------------------------------------------------- /manage/images/deviceIcons/TemperatureAndHumiditySensor2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinHangCode/homebridge-mi-aqara/HEAD/manage/images/deviceIcons/TemperatureAndHumiditySensor2.png -------------------------------------------------------------------------------- /manage/easyui/themes/default/passwordbox.css: -------------------------------------------------------------------------------- 1 | .passwordbox-open { 2 | background: url('images/passwordbox_open.png') no-repeat center center; 3 | } 4 | .passwordbox-close { 5 | background: url('images/passwordbox_close.png') no-repeat center center; 6 | } 7 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/validatebox.css: -------------------------------------------------------------------------------- 1 | .inputbox { 2 | display: inline-block; 3 | vertical-align: middle; 4 | overflow: hidden; 5 | white-space: nowrap; 6 | margin: 0; 7 | padding: 0; 8 | } 9 | .validatebox-invalid { 10 | border-color: #ffa8a8; 11 | background-color: #fff3f3; 12 | color: #000; 13 | } 14 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/splitbutton.css: -------------------------------------------------------------------------------- 1 | .s-btn:hover .m-btn-line, 2 | .s-btn-active .m-btn-line, 3 | .s-btn-plain-active .m-btn-line { 4 | display: inline-block; 5 | } 6 | .l-btn:hover .s-btn-downarrow, 7 | .s-btn-active .s-btn-downarrow, 8 | .s-btn-plain-active .s-btn-downarrow { 9 | border-style: solid; 10 | border-color: #aac5e7; 11 | border-width: 0 0 0 1px; 12 | } 13 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/filebox.css: -------------------------------------------------------------------------------- 1 | .filebox .textbox-value { 2 | vertical-align: top; 3 | position: absolute; 4 | top: 0; 5 | left: -5000px; 6 | } 7 | .filebox-label { 8 | display: inline-block; 9 | position: absolute; 10 | width: 100%; 11 | height: 100%; 12 | cursor: pointer; 13 | left: 0; 14 | top: 0; 15 | z-index: 10; 16 | background: url('images/blank.gif') no-repeat; 17 | } 18 | .l-btn-disabled .filebox-label { 19 | cursor: default; 20 | } 21 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/radiobutton.css: -------------------------------------------------------------------------------- 1 | .radiobutton { 2 | position: relative; 3 | border: 2px solid #ffab3f; 4 | border-radius: 50%; 5 | } 6 | .radiobutton-inner { 7 | position: absolute; 8 | left: 0; 9 | top: 0; 10 | width: 100%; 11 | height: 100%; 12 | background: #ffab3f; 13 | border-radius: 50%; 14 | transform: scale(.6); 15 | } 16 | .radiobutton-disabled { 17 | opacity: 0.6; 18 | } 19 | .radiobutton-value { 20 | position: absolute; 21 | overflow: hidden; 22 | width: 1px; 23 | height: 1px; 24 | left: -999px; 25 | } 26 | -------------------------------------------------------------------------------- /lib/AccessoryUtil.js: -------------------------------------------------------------------------------- 1 | class AccessoryUtil { 2 | constructor() { 3 | this.accessories = {}; 4 | } 5 | 6 | getByUUID(uuid) { 7 | return (uuid in this.accessories) ? this.accessories[uuid] : null; 8 | } 9 | 10 | add(accessory) { 11 | this.accessories[accessory.UUID] = accessory; 12 | return accessory; 13 | } 14 | 15 | remove(uuid) { 16 | delete this.accessories[uuid]; 17 | } 18 | 19 | getAll() { 20 | return this.accessories; 21 | } 22 | } 23 | 24 | module.exports = AccessoryUtil; -------------------------------------------------------------------------------- /manage/login/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MiAqara Manage Login 7 | 8 | 9 | 10 |
11 | Password: 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /manage/css/icon.css: -------------------------------------------------------------------------------- 1 | .icon-gateway { 2 | background:url('../images/icons/gateway.png') no-repeat center center; 3 | background-size: 16px 16px; 4 | } 5 | 6 | .icon-device { 7 | background:url('../images/icons/device.png') no-repeat center center; 8 | background-size: 16px 16px; 9 | } 10 | 11 | .icon-accessory { 12 | background:url('../images/icons/accessory.png') no-repeat center center; 13 | background-size: 16px 16px; 14 | } 15 | 16 | .icon-homekit { 17 | background:url('../images/icons/homekit.png') no-repeat center center; 18 | background-size: 16px 16px; 19 | } -------------------------------------------------------------------------------- /manage/easyui/themes/default/checkbox.css: -------------------------------------------------------------------------------- 1 | .checkbox { 2 | position: relative; 3 | border: 2px solid #ffab3f; 4 | -moz-border-radius: 5px 5px 5px 5px; 5 | -webkit-border-radius: 5px 5px 5px 5px; 6 | border-radius: 5px 5px 5px 5px; 7 | } 8 | .checkbox-checked { 9 | border: 0; 10 | background: #ffab3f; 11 | } 12 | .checkbox-inner { 13 | position: absolute; 14 | left: 0; 15 | top: 0; 16 | width: 100%; 17 | height: 100%; 18 | } 19 | .checkbox path { 20 | stroke-width: 2px; 21 | } 22 | .checkbox-disabled { 23 | opacity: 0.6; 24 | } 25 | .checkbox-value { 26 | position: absolute; 27 | overflow: hidden; 28 | width: 1px; 29 | height: 1px; 30 | left: -999px; 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-mi-aqara", 3 | "version": "0.8.1", 4 | "description": "XiaoMi Aqara plugins for HomeBridge(https://github.com/nfarina/homebridge).", 5 | "license": "GPL", 6 | "keywords": [ 7 | "homebridge-plugin" 8 | ], 9 | "author": "hang.yin", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/YinHangCode/homebridge-mi-aqara" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/YinHangCode/homebridge-mi-aqara/issues" 16 | }, 17 | "engines": { 18 | "node": ">=0.12.0", 19 | "homebridge": ">=0.4.1" 20 | }, 21 | "dependencies": { 22 | "body-parser": "1.18.3", 23 | "express": "4.16.3", 24 | "express-session": "1.15.6", 25 | "mqtt": "2.18.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/combo.css: -------------------------------------------------------------------------------- 1 | .combo-arrow { 2 | width: 18px; 3 | height: 20px; 4 | overflow: hidden; 5 | display: inline-block; 6 | vertical-align: top; 7 | cursor: pointer; 8 | opacity: 0.6; 9 | filter: alpha(opacity=60); 10 | } 11 | .combo-arrow-hover { 12 | opacity: 1.0; 13 | filter: alpha(opacity=100); 14 | } 15 | .combo-panel { 16 | overflow: auto; 17 | } 18 | .combo-arrow { 19 | background: url('images/combo_arrow.png') no-repeat center center; 20 | } 21 | .combo-panel { 22 | background-color: #ffffff; 23 | } 24 | .combo-arrow { 25 | background-color: #E0ECFF; 26 | } 27 | .combo-arrow-hover { 28 | background-color: #eaf2ff; 29 | } 30 | .combo-arrow:hover { 31 | background-color: #eaf2ff; 32 | } 33 | .combo .textbox-icon-disabled:hover { 34 | cursor: default; 35 | } 36 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/datebox.css: -------------------------------------------------------------------------------- 1 | .datebox-calendar-inner { 2 | height: 250px; 3 | } 4 | .datebox-button { 5 | padding: 4px 0; 6 | text-align: center; 7 | } 8 | .datebox-button a { 9 | line-height: 22px; 10 | font-size: 14px; 11 | font-weight: bold; 12 | text-decoration: none; 13 | opacity: 0.6; 14 | filter: alpha(opacity=60); 15 | } 16 | .datebox-button a:hover { 17 | opacity: 1.0; 18 | filter: alpha(opacity=100); 19 | } 20 | .datebox-current, 21 | .datebox-close { 22 | float: left; 23 | } 24 | .datebox-close { 25 | float: right; 26 | } 27 | .datebox .combo-arrow { 28 | background-image: url('images/datebox_arrow.png'); 29 | background-position: center center; 30 | } 31 | .datebox-button { 32 | background-color: #F4F4F4; 33 | } 34 | .datebox-button a { 35 | color: #444; 36 | } 37 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/progressbar.css: -------------------------------------------------------------------------------- 1 | .progressbar { 2 | border-width: 1px; 3 | border-style: solid; 4 | -moz-border-radius: 5px 5px 5px 5px; 5 | -webkit-border-radius: 5px 5px 5px 5px; 6 | border-radius: 5px 5px 5px 5px; 7 | overflow: hidden; 8 | position: relative; 9 | } 10 | .progressbar-text { 11 | text-align: center; 12 | position: absolute; 13 | } 14 | .progressbar-value { 15 | position: relative; 16 | overflow: hidden; 17 | width: 0; 18 | -moz-border-radius: 5px 0 0 5px; 19 | -webkit-border-radius: 5px 0 0 5px; 20 | border-radius: 5px 0 0 5px; 21 | } 22 | .progressbar { 23 | border-color: #95B8E7; 24 | } 25 | .progressbar-text { 26 | color: #000000; 27 | font-size: 14px; 28 | } 29 | .progressbar-value, 30 | .progressbar-value .progressbar-text { 31 | background-color: #ffe48d; 32 | color: #000000; 33 | } 34 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/propertygrid.css: -------------------------------------------------------------------------------- 1 | .propertygrid .datagrid-view1 .datagrid-body td { 2 | padding-bottom: 1px; 3 | border-width: 0 1px 0 0; 4 | } 5 | .propertygrid .datagrid-group { 6 | overflow: hidden; 7 | border-width: 0 0 1px 0; 8 | border-style: solid; 9 | } 10 | .propertygrid .datagrid-group span { 11 | font-weight: bold; 12 | } 13 | .propertygrid .datagrid-view1 .datagrid-body td { 14 | border-color: #dddddd; 15 | } 16 | .propertygrid .datagrid-view1 .datagrid-group { 17 | border-color: #E0ECFF; 18 | } 19 | .propertygrid .datagrid-view2 .datagrid-group { 20 | border-color: #dddddd; 21 | } 22 | .propertygrid .datagrid-group, 23 | .propertygrid .datagrid-view1 .datagrid-body, 24 | .propertygrid .datagrid-view1 .datagrid-row-over, 25 | .propertygrid .datagrid-view1 .datagrid-row-selected { 26 | background: #E0ECFF; 27 | } 28 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/combobox.css: -------------------------------------------------------------------------------- 1 | .combobox-item, 2 | .combobox-group, 3 | .combobox-stick { 4 | font-size: 14px; 5 | padding: 6px 4px; 6 | line-height: 20px; 7 | } 8 | .combobox-item-disabled { 9 | opacity: 0.5; 10 | filter: alpha(opacity=50); 11 | } 12 | .combobox-gitem { 13 | padding-left: 10px; 14 | } 15 | .combobox-group, 16 | .combobox-stick { 17 | font-weight: bold; 18 | } 19 | .combobox-stick { 20 | position: absolute; 21 | top: 1px; 22 | left: 1px; 23 | right: 1px; 24 | background: inherit; 25 | } 26 | .combobox-item-hover { 27 | background-color: #eaf2ff; 28 | color: #000000; 29 | } 30 | .combobox-item-selected { 31 | background-color: #ffe48d; 32 | color: #000000; 33 | } 34 | .combobox-icon { 35 | display: inline-block; 36 | width: 16px; 37 | height: 16px; 38 | vertical-align: middle; 39 | margin-right: 2px; 40 | } 41 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/tagbox.css: -------------------------------------------------------------------------------- 1 | .tagbox { 2 | cursor: text; 3 | } 4 | .tagbox .textbox-text { 5 | float: left; 6 | } 7 | .tagbox-label { 8 | position: relative; 9 | display: block; 10 | margin: 4px 0 0 4px; 11 | padding: 0 20px 0 4px; 12 | float: left; 13 | vertical-align: top; 14 | text-decoration: none; 15 | -moz-border-radius: 5px 5px 5px 5px; 16 | -webkit-border-radius: 5px 5px 5px 5px; 17 | border-radius: 5px 5px 5px 5px; 18 | background: #eaf2ff; 19 | color: #000000; 20 | } 21 | .tagbox-remove { 22 | background: url('images/tagbox_icons.png') no-repeat -16px center; 23 | position: absolute; 24 | display: block; 25 | width: 16px; 26 | height: 16px; 27 | right: 2px; 28 | top: 50%; 29 | margin-top: -8px; 30 | opacity: 0.6; 31 | filter: alpha(opacity=60); 32 | } 33 | .tagbox-remove:hover { 34 | opacity: 1; 35 | filter: alpha(opacity=100); 36 | } 37 | .textbox-disabled .tagbox-label { 38 | cursor: default; 39 | } 40 | .textbox-disabled .tagbox-remove:hover { 41 | cursor: default; 42 | opacity: 0.6; 43 | filter: alpha(opacity=60); 44 | } 45 | -------------------------------------------------------------------------------- /lib/GatewayUtil.js: -------------------------------------------------------------------------------- 1 | class GatewayUtil { 2 | constructor() { 3 | this.gateways = {}; 4 | } 5 | 6 | getBySid(sid) { 7 | return (sid in this.gateways) ? this.gateways[sid] : null; 8 | } 9 | 10 | add(gateway) { 11 | this.gateways[gateway.sid] = gateway; 12 | return gateway; 13 | } 14 | 15 | update(sid, newGateway) { 16 | var gateway = this.getBySid(sid); 17 | if(null != gateway) { 18 | for(var item in newGateway) { 19 | gateway[item] = newGateway[item]; 20 | } 21 | } 22 | return gateway; 23 | } 24 | 25 | addOrUpdate(sid, newGateway) { 26 | var gateway = this.getBySid(sid); 27 | if(null == gateway) { 28 | return this.add(newGateway); 29 | } else { 30 | return this.update(sid, newGateway); 31 | } 32 | } 33 | 34 | remove(sid) { 35 | delete this.gateways[sid]; 36 | } 37 | 38 | getAll() { 39 | return this.gateways; 40 | } 41 | } 42 | 43 | module.exports = GatewayUtil; -------------------------------------------------------------------------------- /manage/easyui/themes/default/messager.css: -------------------------------------------------------------------------------- 1 | .messager-body { 2 | padding: 10px 10px 30px 10px; 3 | overflow: auto; 4 | } 5 | .messager-button { 6 | text-align: center; 7 | padding: 5px; 8 | } 9 | .messager-button .l-btn { 10 | width: 70px; 11 | } 12 | .messager-icon { 13 | float: left; 14 | width: 32px; 15 | height: 32px; 16 | margin: 0 10px 10px 0; 17 | } 18 | .messager-error { 19 | background: url('images/messager_icons.png') no-repeat scroll -64px 0; 20 | } 21 | .messager-info { 22 | background: url('images/messager_icons.png') no-repeat scroll 0 0; 23 | } 24 | .messager-question { 25 | background: url('images/messager_icons.png') no-repeat scroll -32px 0; 26 | } 27 | .messager-warning { 28 | background: url('images/messager_icons.png') no-repeat scroll -96px 0; 29 | } 30 | .messager-progress { 31 | padding: 10px; 32 | } 33 | .messager-p-msg { 34 | margin-bottom: 5px; 35 | } 36 | .messager-body .messager-input { 37 | width: 100%; 38 | padding: 4px 0; 39 | outline-style: none; 40 | border: 1px solid #95B8E7; 41 | } 42 | .window-thinborder .messager-button { 43 | padding-bottom: 8px; 44 | } 45 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/dialog.css: -------------------------------------------------------------------------------- 1 | .dialog-content { 2 | overflow: auto; 3 | } 4 | .dialog-toolbar { 5 | position: relative; 6 | padding: 2px 5px; 7 | } 8 | .dialog-tool-separator { 9 | float: left; 10 | height: 24px; 11 | border-left: 1px solid #ccc; 12 | border-right: 1px solid #fff; 13 | margin: 2px 1px; 14 | } 15 | .dialog-button { 16 | position: relative; 17 | top: -1px; 18 | padding: 5px; 19 | text-align: right; 20 | } 21 | .dialog-button .l-btn { 22 | margin-left: 5px; 23 | } 24 | .dialog-toolbar, 25 | .dialog-button { 26 | background: #F4F4F4; 27 | border-width: 1px; 28 | border-style: solid; 29 | } 30 | .dialog-toolbar { 31 | border-color: #95B8E7 #95B8E7 #dddddd #95B8E7; 32 | } 33 | .dialog-button { 34 | border-color: #dddddd #95B8E7 #95B8E7 #95B8E7; 35 | } 36 | .window-thinborder .dialog-toolbar { 37 | border-left: transparent; 38 | border-right: transparent; 39 | border-top-color: #F4F4F4; 40 | } 41 | .window-thinborder .dialog-button { 42 | top: 0px; 43 | padding: 5px 8px 8px 8px; 44 | border-left: transparent; 45 | border-right: transparent; 46 | border-bottom: transparent; 47 | } 48 | -------------------------------------------------------------------------------- /lib/LogUtil.js: -------------------------------------------------------------------------------- 1 | class LogUtil { 2 | constructor(flag, log) { 3 | this.flag = flag; 4 | this.log = log; 5 | } 6 | 7 | debug(str) { 8 | this.log.debug(this.flag ? "[" + this.flag + "]" : "" + "[DEBUG]" + str); 9 | } 10 | 11 | info(str) { 12 | this.log.info(this.flag ? "[" + this.flag + "]" : "" + "[INFO]" + str); 13 | } 14 | 15 | warn(str) { 16 | this.log.warn(this.flag ? "[" + this.flag + "]" : "" + "[WARN]" + str); 17 | } 18 | 19 | error(str) { 20 | this.log.error(this.flag ? "[" + this.flag + "]" : "" + "[ERROR]" + str); 21 | if(str instanceof Error) { 22 | this.log.debug(this.flag ? "[" + this.flag + "]" : "" + "[ERROR]" + str.stack); 23 | } 24 | } 25 | 26 | objKey2Str(obj) { 27 | var keys = ''; 28 | try { 29 | for(var key in obj) { 30 | keys += key + ', '; 31 | } 32 | keys = keys.substring(0, keys.lastIndexOf(',')); 33 | } catch(e) { 34 | } 35 | 36 | return keys; 37 | } 38 | } 39 | 40 | module.exports = LogUtil; -------------------------------------------------------------------------------- /lib/DeviceUtil.js: -------------------------------------------------------------------------------- 1 | class DeviceUtil { 2 | constructor() { 3 | this.devices = {}; 4 | } 5 | 6 | getBySid(sid) { 7 | return (sid in this.devices) ? this.devices[sid] : null; 8 | } 9 | 10 | add(device) { 11 | this.devices[device.sid] = device; 12 | return device; 13 | } 14 | 15 | update(sid, newDevice) { 16 | var device = this.getBySid(sid); 17 | if(null != device) { 18 | for(var item in newDevice) { 19 | device[item] = newDevice[item]; 20 | } 21 | } 22 | return device; 23 | } 24 | 25 | addOrUpdate(sid, newDevice) { 26 | var device = this.getBySid(sid); 27 | if(null == device) { 28 | return this.add(newDevice); 29 | } else { 30 | return this.update(sid, newDevice); 31 | } 32 | } 33 | 34 | remove(sid) { 35 | delete this.devices[sid]; 36 | } 37 | 38 | getAutoRemoveDevice(threshold) { 39 | var r = {} 40 | 41 | var nowTime = Date.now(); 42 | for(var sid in this.devices) { 43 | var device = this.getBySid(sid); 44 | if ((nowTime - device.lastUpdateTime) > threshold) { 45 | r[sid] = device; 46 | } 47 | } 48 | 49 | return r; 50 | } 51 | 52 | getAll() { 53 | return this.devices; 54 | } 55 | } 56 | 57 | module.exports = DeviceUtil; -------------------------------------------------------------------------------- /parser/DeviceParser.js: -------------------------------------------------------------------------------- 1 | class DeviceParser { 2 | constructor(platform) { 3 | this.platform = platform; 4 | 5 | this.initAccessoriesParser(); 6 | } 7 | 8 | getAccessoriesUUID(deviceSid) { 9 | var r = {}; 10 | 11 | var parsers = this.accessoriesParsers; 12 | for(var item in parsers) { 13 | r[item] = parsers[item].getAccessoryUUID(deviceSid); 14 | } 15 | 16 | return r; 17 | } 18 | 19 | getAccessoriesParserInfo() { 20 | return {}; 21 | } 22 | 23 | initAccessoriesParser() { 24 | this.accessoriesParsers = {}; 25 | 26 | var accessoriesParserInfo = this.getAccessoriesParserInfo(); 27 | for(var key in accessoriesParserInfo) { 28 | this.accessoriesParsers[key] = new (accessoriesParserInfo[key])(this.platform, key); 29 | } 30 | } 31 | 32 | getCreateAccessories(jsonObj) { 33 | var r = []; 34 | 35 | var parsers = this.accessoriesParsers; 36 | for(var item in parsers) { 37 | var accessory = parsers[item].getCreateAccessories(jsonObj); 38 | if(accessory) { 39 | r.push(accessory); 40 | } 41 | } 42 | 43 | return r; 44 | } 45 | 46 | parserAccessories(jsonObj) { 47 | var parsers = this.accessoriesParsers; 48 | for(var item in parsers) { 49 | parsers[item].parserAccessories(jsonObj); 50 | } 51 | } 52 | } 53 | 54 | module.exports = DeviceParser; -------------------------------------------------------------------------------- /manage/easyui/themes/default/searchbox.css: -------------------------------------------------------------------------------- 1 | .searchbox-button { 2 | width: 18px; 3 | height: 20px; 4 | overflow: hidden; 5 | display: inline-block; 6 | vertical-align: top; 7 | cursor: pointer; 8 | opacity: 0.6; 9 | filter: alpha(opacity=60); 10 | } 11 | .searchbox-button-hover { 12 | opacity: 1.0; 13 | filter: alpha(opacity=100); 14 | } 15 | .searchbox .l-btn-plain { 16 | border: 0; 17 | padding: 0; 18 | vertical-align: top; 19 | opacity: 0.6; 20 | filter: alpha(opacity=60); 21 | -moz-border-radius: 0 0 0 0; 22 | -webkit-border-radius: 0 0 0 0; 23 | border-radius: 0 0 0 0; 24 | } 25 | .searchbox .l-btn-plain:hover { 26 | border: 0; 27 | padding: 0; 28 | opacity: 1.0; 29 | filter: alpha(opacity=100); 30 | -moz-border-radius: 0 0 0 0; 31 | -webkit-border-radius: 0 0 0 0; 32 | border-radius: 0 0 0 0; 33 | } 34 | .searchbox a.m-btn-plain-active { 35 | -moz-border-radius: 0 0 0 0; 36 | -webkit-border-radius: 0 0 0 0; 37 | border-radius: 0 0 0 0; 38 | } 39 | .searchbox .m-btn-active { 40 | border-width: 0 1px 0 0; 41 | -moz-border-radius: 0 0 0 0; 42 | -webkit-border-radius: 0 0 0 0; 43 | border-radius: 0 0 0 0; 44 | } 45 | .searchbox .textbox-button-right { 46 | border-width: 0 0 0 1px; 47 | } 48 | .searchbox .textbox-button-left { 49 | border-width: 0 1px 0 0; 50 | } 51 | .searchbox-button { 52 | background: url('images/searchbox_button.png') no-repeat center center; 53 | } 54 | .searchbox .l-btn-plain { 55 | background: #E0ECFF; 56 | } 57 | .searchbox .l-btn-plain-disabled, 58 | .searchbox .l-btn-plain-disabled:hover { 59 | opacity: 0.5; 60 | filter: alpha(opacity=50); 61 | } 62 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/sidemenu.css: -------------------------------------------------------------------------------- 1 | .sidemenu .tree-hit { 2 | background-image: none; 3 | } 4 | .sidemenu-default-icon { 5 | background-image: none; 6 | width: 0; 7 | } 8 | .sidemenu .accordion .accordion-header, 9 | .sidemenu .accordion .accordion-body { 10 | border-bottom-color: transparent; 11 | background: transparent; 12 | } 13 | .sidemenu .accordion .accordion-header { 14 | color: #0E2D5F; 15 | } 16 | .sidemenu .accordion-header .panel-title { 17 | height: 30px; 18 | line-height: 30px; 19 | color: #0E2D5F; 20 | } 21 | .sidemenu .accordion-header:hover { 22 | background: #eaf2ff; 23 | color: #0E2D5F; 24 | } 25 | .sidemenu .tree-node-hover { 26 | background: #eaf2ff; 27 | color: #0E2D5F; 28 | } 29 | .sidemenu .tree-node-selected { 30 | border-right: 2px solid #ffab3f; 31 | color: #000000; 32 | background: #ffe48d; 33 | } 34 | .sidemenu .tree-node { 35 | height: 40px; 36 | } 37 | .sidemenu .tree-title { 38 | margin: 11px 0; 39 | } 40 | .sidemenu .tree-node-nonleaf { 41 | position: relative; 42 | } 43 | .sidemenu .tree-node-nonleaf::after { 44 | display: inline-block; 45 | content: ''; 46 | position: absolute; 47 | top: 50%; 48 | margin-top: -8px; 49 | background: url('images/accordion_arrows.png') no-repeat 0 0; 50 | width: 16px; 51 | height: 16px; 52 | right: 5px; 53 | } 54 | .sidemenu .tree-node-nonleaf-collapsed::after { 55 | background: url('images/accordion_arrows.png') no-repeat -16px 0; 56 | } 57 | .sidemenu-collapsed .panel-icon { 58 | left: 50%; 59 | margin-left: -8px; 60 | } 61 | .sidemenu-tooltip { 62 | padding: 0; 63 | margin: 0 -12px; 64 | border: 0; 65 | } 66 | .sidemenu-tooltip.tooltip-left { 67 | margin: 0 12px; 68 | } 69 | .sidemenu-tooltip .tooltip-arrow-outer, 70 | .sidemenu-tooltip .tooltip-arrow { 71 | display: none; 72 | } 73 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/pagination.css: -------------------------------------------------------------------------------- 1 | .pagination { 2 | zoom: 1; 3 | padding: 2px; 4 | } 5 | .pagination table { 6 | float: left; 7 | height: 30px; 8 | } 9 | .pagination td { 10 | border: 0; 11 | } 12 | .pagination-btn-separator { 13 | float: left; 14 | height: 24px; 15 | border-left: 1px solid #ccc; 16 | border-right: 1px solid #fff; 17 | margin: 3px 1px; 18 | } 19 | .pagination .pagination-num { 20 | border-width: 1px; 21 | border-style: solid; 22 | margin: 0 2px; 23 | padding: 2px; 24 | width: 3em; 25 | height: auto; 26 | text-align: center; 27 | font-size: 14px; 28 | } 29 | .pagination-page-list { 30 | margin: 0px 6px; 31 | padding: 1px 2px; 32 | width: auto; 33 | height: auto; 34 | border-width: 1px; 35 | border-style: solid; 36 | } 37 | .pagination-info { 38 | float: right; 39 | margin: 0 6px; 40 | padding: 0; 41 | height: 30px; 42 | line-height: 30px; 43 | font-size: 14px; 44 | } 45 | .pagination span { 46 | font-size: 14px; 47 | } 48 | .pagination-link .l-btn-text { 49 | box-sizing: border-box; 50 | text-align: center; 51 | margin: 0; 52 | padding: 0 .5em; 53 | width: auto; 54 | min-width: 28px; 55 | } 56 | .pagination-first { 57 | background: url('images/pagination_icons.png') no-repeat 0 center; 58 | } 59 | .pagination-prev { 60 | background: url('images/pagination_icons.png') no-repeat -16px center; 61 | } 62 | .pagination-next { 63 | background: url('images/pagination_icons.png') no-repeat -32px center; 64 | } 65 | .pagination-last { 66 | background: url('images/pagination_icons.png') no-repeat -48px center; 67 | } 68 | .pagination-load { 69 | background: url('images/pagination_icons.png') no-repeat -64px center; 70 | } 71 | .pagination-loading { 72 | background: url('images/loading.gif') no-repeat center center; 73 | } 74 | .pagination-page-list, 75 | .pagination .pagination-num { 76 | border-color: #95B8E7; 77 | } 78 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/switchbutton.css: -------------------------------------------------------------------------------- 1 | .switchbutton { 2 | text-decoration: none; 3 | display: inline-block; 4 | overflow: hidden; 5 | vertical-align: middle; 6 | margin: 0; 7 | padding: 0; 8 | cursor: pointer; 9 | background: #bbb; 10 | border: 1px solid #bbb; 11 | -moz-border-radius: 5px 5px 5px 5px; 12 | -webkit-border-radius: 5px 5px 5px 5px; 13 | border-radius: 5px 5px 5px 5px; 14 | } 15 | .switchbutton-inner { 16 | display: inline-block; 17 | overflow: hidden; 18 | position: relative; 19 | top: -1px; 20 | left: -1px; 21 | } 22 | .switchbutton-on, 23 | .switchbutton-off, 24 | .switchbutton-handle { 25 | display: inline-block; 26 | text-align: center; 27 | height: 100%; 28 | float: left; 29 | font-size: 14px; 30 | -moz-border-radius: 5px 5px 5px 5px; 31 | -webkit-border-radius: 5px 5px 5px 5px; 32 | border-radius: 5px 5px 5px 5px; 33 | } 34 | .switchbutton-on { 35 | background: #ffe48d; 36 | color: #000000; 37 | } 38 | .switchbutton-off { 39 | background-color: #ffffff; 40 | color: #000000; 41 | } 42 | .switchbutton-on, 43 | .switchbutton-reversed .switchbutton-off { 44 | -moz-border-radius: 5px 0 0 5px; 45 | -webkit-border-radius: 5px 0 0 5px; 46 | border-radius: 5px 0 0 5px; 47 | } 48 | .switchbutton-off, 49 | .switchbutton-reversed .switchbutton-on { 50 | -moz-border-radius: 0 5px 5px 0; 51 | -webkit-border-radius: 0 5px 5px 0; 52 | border-radius: 0 5px 5px 0; 53 | } 54 | .switchbutton-handle { 55 | position: absolute; 56 | top: 0; 57 | left: 50%; 58 | background-color: #ffffff; 59 | color: #000000; 60 | border: 1px solid #bbb; 61 | -moz-box-shadow: 0 0 3px 0 #bbb; 62 | -webkit-box-shadow: 0 0 3px 0 #bbb; 63 | box-shadow: 0 0 3px 0 #bbb; 64 | } 65 | .switchbutton-value { 66 | position: absolute; 67 | top: 0; 68 | left: -5000px; 69 | } 70 | .switchbutton-disabled { 71 | opacity: 0.5; 72 | filter: alpha(opacity=50); 73 | } 74 | .switchbutton-disabled, 75 | .switchbutton-readonly { 76 | cursor: default; 77 | } 78 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/slider.css: -------------------------------------------------------------------------------- 1 | .slider-disabled { 2 | opacity: 0.5; 3 | filter: alpha(opacity=50); 4 | } 5 | .slider-h { 6 | height: 22px; 7 | } 8 | .slider-v { 9 | width: 22px; 10 | } 11 | .slider-inner { 12 | position: relative; 13 | height: 6px; 14 | top: 7px; 15 | border-width: 1px; 16 | border-style: solid; 17 | border-radius: 5px; 18 | } 19 | .slider-handle { 20 | position: absolute; 21 | display: block; 22 | outline: none; 23 | width: 20px; 24 | height: 20px; 25 | top: 50%; 26 | margin-top: -10px; 27 | margin-left: -10px; 28 | } 29 | .slider-tip { 30 | position: absolute; 31 | display: inline-block; 32 | line-height: 12px; 33 | font-size: 14px; 34 | white-space: nowrap; 35 | top: -22px; 36 | } 37 | .slider-rule { 38 | position: relative; 39 | top: 15px; 40 | } 41 | .slider-rule span { 42 | position: absolute; 43 | display: inline-block; 44 | font-size: 0; 45 | height: 5px; 46 | border-width: 0 0 0 1px; 47 | border-style: solid; 48 | } 49 | .slider-rulelabel { 50 | position: relative; 51 | top: 20px; 52 | } 53 | .slider-rulelabel span { 54 | position: absolute; 55 | display: inline-block; 56 | font-size: 14px; 57 | } 58 | .slider-v .slider-inner { 59 | width: 6px; 60 | left: 7px; 61 | top: 0; 62 | float: left; 63 | } 64 | .slider-v .slider-handle { 65 | left: 50%; 66 | margin-top: -10px; 67 | } 68 | .slider-v .slider-tip { 69 | left: -10px; 70 | margin-top: -6px; 71 | } 72 | .slider-v .slider-rule { 73 | float: left; 74 | top: 0; 75 | left: 16px; 76 | } 77 | .slider-v .slider-rule span { 78 | width: 5px; 79 | height: 'auto'; 80 | border-left: 0; 81 | border-width: 1px 0 0 0; 82 | border-style: solid; 83 | } 84 | .slider-v .slider-rulelabel { 85 | float: left; 86 | top: 0; 87 | left: 23px; 88 | } 89 | .slider-handle { 90 | background: url('images/slider_handle.png') no-repeat; 91 | } 92 | .slider-inner { 93 | border-color: #95B8E7; 94 | background: #E0ECFF; 95 | } 96 | .slider-rule span { 97 | border-color: #95B8E7; 98 | } 99 | .slider-rulelabel span { 100 | color: #000000; 101 | } 102 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/datalist.css: -------------------------------------------------------------------------------- 1 | .datalist .datagrid-header { 2 | border-width: 0; 3 | } 4 | .datalist .datagrid-group, 5 | .m-list .m-list-group { 6 | height: 25px; 7 | line-height: 25px; 8 | font-weight: bold; 9 | overflow: hidden; 10 | background-color: #efefef; 11 | border-style: solid; 12 | border-width: 0 0 1px 0; 13 | border-color: #ccc; 14 | } 15 | .datalist .datagrid-group-expander { 16 | display: none; 17 | } 18 | .datalist .datagrid-group-title { 19 | padding: 0 4px; 20 | } 21 | .datalist .datagrid-btable { 22 | width: 100%; 23 | table-layout: fixed; 24 | } 25 | .datalist .datagrid-row td { 26 | border-style: solid; 27 | border-left-color: transparent; 28 | border-right-color: transparent; 29 | border-bottom-width: 0; 30 | } 31 | .datalist-lines .datagrid-row td { 32 | border-bottom-width: 1px; 33 | } 34 | .datalist .datagrid-cell, 35 | .m-list li { 36 | width: auto; 37 | height: auto; 38 | padding: 2px 4px; 39 | line-height: 18px; 40 | position: relative; 41 | white-space: nowrap; 42 | text-overflow: ellipsis; 43 | overflow: hidden; 44 | } 45 | .datalist-link, 46 | .m-list li>a { 47 | display: block; 48 | position: relative; 49 | cursor: pointer; 50 | color: #000000; 51 | text-decoration: none; 52 | overflow: hidden; 53 | margin: -2px -4px; 54 | padding: 2px 4px; 55 | padding-right: 16px; 56 | line-height: 18px; 57 | white-space: nowrap; 58 | text-overflow: ellipsis; 59 | overflow: hidden; 60 | } 61 | .datalist-link::after, 62 | .m-list li>a::after { 63 | position: absolute; 64 | display: block; 65 | width: 8px; 66 | height: 8px; 67 | content: ''; 68 | right: 6px; 69 | top: 50%; 70 | margin-top: -4px; 71 | border-style: solid; 72 | border-width: 1px 1px 0 0; 73 | -ms-transform: rotate(45deg); 74 | -moz-transform: rotate(45deg); 75 | -webkit-transform: rotate(45deg); 76 | -o-transform: rotate(45deg); 77 | transform: rotate(45deg); 78 | } 79 | .m-list { 80 | margin: 0; 81 | padding: 0; 82 | list-style: none; 83 | } 84 | .m-list li { 85 | border-style: solid; 86 | border-width: 0 0 1px 0; 87 | border-color: #ccc; 88 | } 89 | .m-list li>a:hover { 90 | background: #eaf2ff; 91 | color: #000000; 92 | } 93 | .m-list .m-list-group { 94 | padding: 0 4px; 95 | } 96 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/menubutton.css: -------------------------------------------------------------------------------- 1 | .m-btn-downarrow, 2 | .s-btn-downarrow { 3 | display: inline-block; 4 | position: absolute; 5 | width: 16px; 6 | height: 16px; 7 | font-size: 1px; 8 | right: 0; 9 | top: 50%; 10 | margin-top: -8px; 11 | } 12 | .m-btn-active, 13 | .s-btn-active { 14 | background: #eaf2ff; 15 | color: #000000; 16 | border: 1px solid #b7d2ff; 17 | filter: none; 18 | } 19 | .m-btn-plain-active, 20 | .s-btn-plain-active { 21 | background: transparent; 22 | padding: 0; 23 | border-width: 1px; 24 | border-style: solid; 25 | -moz-border-radius: 5px 5px 5px 5px; 26 | -webkit-border-radius: 5px 5px 5px 5px; 27 | border-radius: 5px 5px 5px 5px; 28 | } 29 | .m-btn .l-btn-left .l-btn-text { 30 | margin-right: 20px; 31 | } 32 | .m-btn .l-btn-icon-right .l-btn-text { 33 | margin-right: 40px; 34 | } 35 | .m-btn .l-btn-icon-right .l-btn-icon { 36 | right: 20px; 37 | } 38 | .m-btn .l-btn-icon-top .l-btn-text { 39 | margin-right: 4px; 40 | margin-bottom: 14px; 41 | } 42 | .m-btn .l-btn-icon-bottom .l-btn-text { 43 | margin-right: 4px; 44 | margin-bottom: 34px; 45 | } 46 | .m-btn .l-btn-icon-bottom .l-btn-icon { 47 | top: auto; 48 | bottom: 20px; 49 | } 50 | .m-btn .l-btn-icon-top .m-btn-downarrow, 51 | .m-btn .l-btn-icon-bottom .m-btn-downarrow { 52 | top: auto; 53 | bottom: 0px; 54 | left: 50%; 55 | margin-left: -8px; 56 | } 57 | .m-btn-line { 58 | display: inline-block; 59 | position: absolute; 60 | font-size: 1px; 61 | display: none; 62 | } 63 | .m-btn .l-btn-left .m-btn-line { 64 | right: 0; 65 | width: 16px; 66 | height: 500px; 67 | border-style: solid; 68 | border-color: #aac5e7; 69 | border-width: 0 0 0 1px; 70 | } 71 | .m-btn .l-btn-icon-top .m-btn-line, 72 | .m-btn .l-btn-icon-bottom .m-btn-line { 73 | left: 0; 74 | bottom: 0; 75 | width: 500px; 76 | height: 16px; 77 | border-width: 1px 0 0 0; 78 | } 79 | .m-btn-large .l-btn-icon-right .l-btn-text { 80 | margin-right: 56px; 81 | } 82 | .m-btn-large .l-btn-icon-bottom .l-btn-text { 83 | margin-bottom: 50px; 84 | } 85 | .m-btn-downarrow, 86 | .s-btn-downarrow { 87 | background: url('images/menu_arrows.png') no-repeat 0 center; 88 | } 89 | .m-btn-plain-active, 90 | .s-btn-plain-active { 91 | border-color: #b7d2ff; 92 | background-color: #eaf2ff; 93 | color: #000000; 94 | } 95 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/tooltip.css: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | position: absolute; 3 | display: none; 4 | z-index: 9900000; 5 | outline: none; 6 | opacity: 1; 7 | filter: alpha(opacity=100); 8 | padding: 5px; 9 | border-width: 1px; 10 | border-style: solid; 11 | border-radius: 5px; 12 | -moz-border-radius: 5px 5px 5px 5px; 13 | -webkit-border-radius: 5px 5px 5px 5px; 14 | border-radius: 5px 5px 5px 5px; 15 | } 16 | .tooltip-content { 17 | font-size: 14px; 18 | } 19 | .tooltip-arrow-outer, 20 | .tooltip-arrow { 21 | position: absolute; 22 | width: 0; 23 | height: 0; 24 | line-height: 0; 25 | font-size: 0; 26 | border-style: solid; 27 | border-width: 6px; 28 | border-color: transparent; 29 | _border-color: tomato; 30 | _filter: chroma(color=tomato); 31 | } 32 | .tooltip-arrow { 33 | display: none \9; 34 | } 35 | .tooltip-right .tooltip-arrow-outer { 36 | left: 0; 37 | top: 50%; 38 | margin: -6px 0 0 -13px; 39 | } 40 | .tooltip-right .tooltip-arrow { 41 | left: 0; 42 | top: 50%; 43 | margin: -6px 0 0 -12px; 44 | } 45 | .tooltip-left .tooltip-arrow-outer { 46 | right: 0; 47 | top: 50%; 48 | margin: -6px -13px 0 0; 49 | } 50 | .tooltip-left .tooltip-arrow { 51 | right: 0; 52 | top: 50%; 53 | margin: -6px -12px 0 0; 54 | } 55 | .tooltip-top .tooltip-arrow-outer { 56 | bottom: 0; 57 | left: 50%; 58 | margin: 0 0 -13px -6px; 59 | } 60 | .tooltip-top .tooltip-arrow { 61 | bottom: 0; 62 | left: 50%; 63 | margin: 0 0 -12px -6px; 64 | } 65 | .tooltip-bottom .tooltip-arrow-outer { 66 | top: 0; 67 | left: 50%; 68 | margin: -13px 0 0 -6px; 69 | } 70 | .tooltip-bottom .tooltip-arrow { 71 | top: 0; 72 | left: 50%; 73 | margin: -12px 0 0 -6px; 74 | } 75 | .tooltip { 76 | background-color: #ffffff; 77 | border-color: #95B8E7; 78 | color: #000000; 79 | } 80 | .tooltip-right .tooltip-arrow-outer { 81 | border-right-color: #95B8E7; 82 | } 83 | .tooltip-right .tooltip-arrow { 84 | border-right-color: #ffffff; 85 | } 86 | .tooltip-left .tooltip-arrow-outer { 87 | border-left-color: #95B8E7; 88 | } 89 | .tooltip-left .tooltip-arrow { 90 | border-left-color: #ffffff; 91 | } 92 | .tooltip-top .tooltip-arrow-outer { 93 | border-top-color: #95B8E7; 94 | } 95 | .tooltip-top .tooltip-arrow { 96 | border-top-color: #ffffff; 97 | } 98 | .tooltip-bottom .tooltip-arrow-outer { 99 | border-bottom-color: #95B8E7; 100 | } 101 | .tooltip-bottom .tooltip-arrow { 102 | border-bottom-color: #ffffff; 103 | } 104 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/accordion.css: -------------------------------------------------------------------------------- 1 | .accordion { 2 | overflow: hidden; 3 | border-width: 1px; 4 | border-style: solid; 5 | } 6 | .accordion .accordion-header { 7 | border-width: 0 0 1px; 8 | cursor: pointer; 9 | } 10 | .accordion .accordion-body { 11 | border-width: 0 0 1px; 12 | } 13 | .accordion-noborder { 14 | border-width: 0; 15 | } 16 | .accordion-noborder .accordion-header { 17 | border-width: 0 0 1px; 18 | } 19 | .accordion-noborder .accordion-body { 20 | border-width: 0 0 1px; 21 | } 22 | .accordion-collapse { 23 | background: url('images/accordion_arrows.png') no-repeat 0 0; 24 | } 25 | .accordion-expand { 26 | background: url('images/accordion_arrows.png') no-repeat -16px 0; 27 | } 28 | .accordion { 29 | background: #ffffff; 30 | border-color: #95B8E7; 31 | } 32 | .accordion .accordion-header { 33 | background: #E0ECFF; 34 | filter: none; 35 | } 36 | .accordion .accordion-header-selected { 37 | background: #ffe48d; 38 | } 39 | .accordion .accordion-header-selected .panel-title { 40 | color: #000000; 41 | } 42 | .accordion .panel-last > .accordion-header { 43 | border-bottom-color: #E0ECFF; 44 | } 45 | .accordion .panel-last > .accordion-body { 46 | border-bottom-color: #ffffff; 47 | } 48 | .accordion .panel-last > .accordion-header-selected, 49 | .accordion .panel-last > .accordion-header-border { 50 | border-bottom-color: #95B8E7; 51 | } 52 | .accordion> .panel-hleft { 53 | float: left; 54 | } 55 | .accordion> .panel-hleft>.panel-header { 56 | border-width: 0 1px 0 0; 57 | } 58 | .accordion> .panel-hleft> .panel-body { 59 | border-width: 0 1px 0 0; 60 | } 61 | .accordion> .panel-hleft.panel-last > .accordion-header { 62 | border-right-color: #E0ECFF; 63 | } 64 | .accordion> .panel-hleft.panel-last > .accordion-body { 65 | border-right-color: #ffffff; 66 | } 67 | .accordion> .panel-hleft.panel-last > .accordion-header-selected, 68 | .accordion> .panel-hleft.panel-last > .accordion-header-border { 69 | border-right-color: #95B8E7; 70 | } 71 | .accordion> .panel-hright { 72 | float: right; 73 | } 74 | .accordion> .panel-hright>.panel-header { 75 | border-width: 0 0 0 1px; 76 | } 77 | .accordion> .panel-hright> .panel-body { 78 | border-width: 0 0 0 1px; 79 | } 80 | .accordion> .panel-hright.panel-last > .accordion-header { 81 | border-left-color: #E0ECFF; 82 | } 83 | .accordion> .panel-hright.panel-last > .accordion-body { 84 | border-left-color: #ffffff; 85 | } 86 | .accordion> .panel-hright.panel-last > .accordion-header-selected, 87 | .accordion> .panel-hright.panel-last > .accordion-header-border { 88 | border-left-color: #95B8E7; 89 | } 90 | -------------------------------------------------------------------------------- /manage/easyui/themes/icon.css: -------------------------------------------------------------------------------- 1 | .icon-blank{ 2 | background:url('icons/blank.gif') no-repeat center center; 3 | } 4 | .icon-add{ 5 | background:url('icons/edit_add.png') no-repeat center center; 6 | } 7 | .icon-edit{ 8 | background:url('icons/pencil.png') no-repeat center center; 9 | } 10 | .icon-clear{ 11 | background:url('icons/clear.png') no-repeat center center; 12 | } 13 | .icon-remove{ 14 | background:url('icons/edit_remove.png') no-repeat center center; 15 | } 16 | .icon-save{ 17 | background:url('icons/filesave.png') no-repeat center center; 18 | } 19 | .icon-cut{ 20 | background:url('icons/cut.png') no-repeat center center; 21 | } 22 | .icon-ok{ 23 | background:url('icons/ok.png') no-repeat center center; 24 | } 25 | .icon-no{ 26 | background:url('icons/no.png') no-repeat center center; 27 | } 28 | .icon-cancel{ 29 | background:url('icons/cancel.png') no-repeat center center; 30 | } 31 | .icon-reload{ 32 | background:url('icons/reload.png') no-repeat center center; 33 | } 34 | .icon-search{ 35 | background:url('icons/search.png') no-repeat center center; 36 | } 37 | .icon-print{ 38 | background:url('icons/print.png') no-repeat center center; 39 | } 40 | .icon-help{ 41 | background:url('icons/help.png') no-repeat center center; 42 | } 43 | .icon-undo{ 44 | background:url('icons/undo.png') no-repeat center center; 45 | } 46 | .icon-redo{ 47 | background:url('icons/redo.png') no-repeat center center; 48 | } 49 | .icon-back{ 50 | background:url('icons/back.png') no-repeat center center; 51 | } 52 | .icon-sum{ 53 | background:url('icons/sum.png') no-repeat center center; 54 | } 55 | .icon-tip{ 56 | background:url('icons/tip.png') no-repeat center center; 57 | } 58 | .icon-filter{ 59 | background:url('icons/filter.png') no-repeat center center; 60 | } 61 | .icon-man{ 62 | background:url('icons/man.png') no-repeat center center; 63 | } 64 | .icon-lock{ 65 | background:url('icons/lock.png') no-repeat center center; 66 | } 67 | .icon-more{ 68 | background:url('icons/more.png') no-repeat center center; 69 | } 70 | 71 | 72 | .icon-mini-add{ 73 | background:url('icons/mini_add.png') no-repeat center center; 74 | } 75 | .icon-mini-edit{ 76 | background:url('icons/mini_edit.png') no-repeat center center; 77 | } 78 | .icon-mini-refresh{ 79 | background:url('icons/mini_refresh.png') no-repeat center center; 80 | } 81 | 82 | .icon-large-picture{ 83 | background:url('icons/large_picture.png') no-repeat center center; 84 | } 85 | .icon-large-clipart{ 86 | background:url('icons/large_clipart.png') no-repeat center center; 87 | } 88 | .icon-large-shapes{ 89 | background:url('icons/large_shapes.png') no-repeat center center; 90 | } 91 | .icon-large-smartart{ 92 | background:url('icons/large_smartart.png') no-repeat center center; 93 | } 94 | .icon-large-chart{ 95 | background:url('icons/large_chart.png') no-repeat center center; 96 | } 97 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/menu.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | position: absolute; 3 | margin: 0; 4 | padding: 2px; 5 | border-width: 1px; 6 | border-style: solid; 7 | overflow: hidden; 8 | } 9 | .menu-inline { 10 | position: relative; 11 | } 12 | .menu-item { 13 | position: relative; 14 | margin: 0; 15 | padding: 0; 16 | overflow: hidden; 17 | white-space: nowrap; 18 | cursor: pointer; 19 | border-width: 1px; 20 | border-style: solid; 21 | } 22 | .menu-text { 23 | height: 20px; 24 | line-height: 20px; 25 | float: left; 26 | padding-left: 28px; 27 | } 28 | .menu-icon { 29 | position: absolute; 30 | width: 16px; 31 | height: 16px; 32 | left: 2px; 33 | top: 50%; 34 | margin-top: -8px; 35 | } 36 | .menu-rightarrow { 37 | position: absolute; 38 | width: 16px; 39 | height: 16px; 40 | right: 0; 41 | top: 50%; 42 | margin-top: -8px; 43 | } 44 | .menu-line { 45 | position: absolute; 46 | left: 26px; 47 | top: 0; 48 | height: 2000px; 49 | font-size: 1px; 50 | } 51 | .menu-sep { 52 | margin: 3px 0px 3px 25px; 53 | font-size: 1px; 54 | } 55 | .menu-noline .menu-line { 56 | display: none; 57 | } 58 | .menu-noline .menu-sep { 59 | margin-left: 0; 60 | margin-right: 0; 61 | } 62 | .menu-active { 63 | -moz-border-radius: 5px 5px 5px 5px; 64 | -webkit-border-radius: 5px 5px 5px 5px; 65 | border-radius: 5px 5px 5px 5px; 66 | } 67 | .menu-item-disabled { 68 | opacity: 0.5; 69 | filter: alpha(opacity=50); 70 | cursor: default; 71 | } 72 | .menu-text, 73 | .menu-text span { 74 | font-size: 14px; 75 | } 76 | .menu-shadow { 77 | position: absolute; 78 | -moz-border-radius: 5px 5px 5px 5px; 79 | -webkit-border-radius: 5px 5px 5px 5px; 80 | border-radius: 5px 5px 5px 5px; 81 | background: #ccc; 82 | -moz-box-shadow: 2px 2px 3px #cccccc; 83 | -webkit-box-shadow: 2px 2px 3px #cccccc; 84 | box-shadow: 2px 2px 3px #cccccc; 85 | filter: progid:DXImageTransform.Microsoft.Blur(pixelRadius=2,MakeShadow=false,ShadowOpacity=0.2); 86 | } 87 | .menu-rightarrow { 88 | background: url('images/menu_arrows.png') no-repeat -32px center; 89 | } 90 | .menu-line { 91 | border-left: 1px solid #ccc; 92 | border-right: 1px solid #fff; 93 | } 94 | .menu-sep { 95 | border-top: 1px solid #ccc; 96 | border-bottom: 1px solid #fff; 97 | } 98 | .menu { 99 | background-color: #fafafa; 100 | border-color: #ddd; 101 | color: #444; 102 | } 103 | .menu-content { 104 | background: #ffffff; 105 | } 106 | .menu-item { 107 | border-color: transparent; 108 | _border-color: #fafafa; 109 | } 110 | .menu-active { 111 | border-color: #b7d2ff; 112 | color: #000000; 113 | background: #eaf2ff; 114 | } 115 | .menu-active-disabled { 116 | border-color: transparent; 117 | background: transparent; 118 | color: #444; 119 | } 120 | -------------------------------------------------------------------------------- /parser/SwitchVirtualBasePressParser.js: -------------------------------------------------------------------------------- 1 | const AccessoryParser = require('./AccessoryParser'); 2 | 3 | class SwitchVirtualBasePressParser extends AccessoryParser { 4 | constructor(platform, accessoryType) { 5 | super(platform, accessoryType) 6 | } 7 | 8 | getAccessoryCategory(deviceSid) { 9 | return this.Accessory.Categories.SWITCH; 10 | } 11 | 12 | getServices(jsonObj, accessoryName) { 13 | var that = this; 14 | var result = []; 15 | 16 | var service = new that.Service.Switch(accessoryName); 17 | service.getCharacteristic(that.Characteristic.On); 18 | result.push(service); 19 | 20 | var batteryService = new that.Service.BatteryService(accessoryName); 21 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 22 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 23 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 24 | result.push(batteryService); 25 | 26 | return result; 27 | } 28 | 29 | parserAccessories(jsonObj) { 30 | var that = this; 31 | var deviceSid = jsonObj['sid']; 32 | var uuid = that.getAccessoryUUID(deviceSid); 33 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 34 | if(accessory) { 35 | var service = accessory.getService(that.Service.Switch); 36 | var onCharacteristic = service.getCharacteristic(that.Characteristic.On); 37 | 38 | if(onCharacteristic.listeners('set').length == 0) { 39 | onCharacteristic.on("set", function(value, callback) { 40 | var command = that.getWriteCommand(deviceSid, value); 41 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 42 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 43 | that.callback2HB(deviceSid, this, callback, null); 44 | that.doSomething(jsonObj); 45 | setTimeout(() => { 46 | onCharacteristic.updateValue(false); 47 | }, 10); 48 | } else { 49 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 50 | that.callback2HB(deviceSid, this, callback, null); 51 | that.doSomething(jsonObj); 52 | setTimeout(() => { 53 | onCharacteristic.updateValue(false); 54 | }, 10); 55 | }).catch(function(err) { 56 | that.platform.log.error(err); 57 | that.callback2HB(deviceSid, this, callback, err); 58 | }); 59 | } 60 | }); 61 | } 62 | 63 | that.parserBatteryService(accessory, jsonObj); 64 | } 65 | } 66 | 67 | doSomething(jsonObj) { 68 | } 69 | } 70 | 71 | module.exports = SwitchVirtualBasePressParser; 72 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/spinner.css: -------------------------------------------------------------------------------- 1 | .spinner-arrow { 2 | display: inline-block; 3 | overflow: hidden; 4 | vertical-align: top; 5 | margin: 0; 6 | padding: 0; 7 | opacity: 1.0; 8 | filter: alpha(opacity=100); 9 | width: 18px; 10 | } 11 | .spinner-arrow.spinner-button-top, 12 | .spinner-arrow.spinner-button-bottom, 13 | .spinner-arrow.spinner-button-left, 14 | .spinner-arrow.spinner-button-right { 15 | background-color: #E0ECFF; 16 | } 17 | .spinner-arrow-up, 18 | .spinner-arrow-down { 19 | opacity: 0.6; 20 | filter: alpha(opacity=60); 21 | display: block; 22 | font-size: 1px; 23 | width: 18px; 24 | height: 10px; 25 | width: 100%; 26 | height: 50%; 27 | color: #444; 28 | outline-style: none; 29 | background-color: #E0ECFF; 30 | } 31 | .spinner-button-updown { 32 | opacity: 1.0; 33 | } 34 | .spinner-button-updown .spinner-button-top, 35 | .spinner-button-updown .spinner-button-bottom { 36 | position: relative; 37 | display: block; 38 | width: 100%; 39 | height: 50%; 40 | } 41 | .spinner-button-updown .spinner-arrow-up, 42 | .spinner-button-updown .spinner-arrow-down { 43 | opacity: 1.0; 44 | filter: alpha(opacity=100); 45 | cursor: pointer; 46 | width: 16px; 47 | height: 16px; 48 | top: 50%; 49 | left: 50%; 50 | margin-top: -8px; 51 | margin-left: -8px; 52 | position: absolute; 53 | } 54 | .spinner-button-updown .spinner-button-top, 55 | .spinner-button-updown .spinner-button-bottom { 56 | cursor: pointer; 57 | opacity: 0.6; 58 | filter: alpha(opacity=60); 59 | } 60 | .spinner-button-updown .spinner-button-top:hover, 61 | .spinner-button-updown .spinner-button-bottom:hover { 62 | opacity: 1.0; 63 | filter: alpha(opacity=100); 64 | } 65 | .spinner-button-updown .spinner-arrow-up, 66 | .spinner-button-updown .spinner-arrow-down, 67 | .spinner-button-updown .spinner-arrow-up:hover, 68 | .spinner-button-updown .spinner-arrow-down:hover { 69 | background-color: transparent; 70 | } 71 | .spinner-arrow-hover { 72 | background-color: #eaf2ff; 73 | opacity: 1.0; 74 | filter: alpha(opacity=100); 75 | } 76 | .spinner-button-top:hover, 77 | .spinner-button-bottom:hover, 78 | .spinner-button-left:hover, 79 | .spinner-button-right:hover, 80 | .spinner-arrow-up:hover, 81 | .spinner-arrow-down:hover { 82 | opacity: 1.0; 83 | filter: alpha(opacity=100); 84 | background-color: #eaf2ff; 85 | } 86 | .textbox-disabled .spinner-button-top:hover, 87 | .textbox-disabled .spinner-button-bottom:hover, 88 | .textbox-disabled .spinner-button-left:hover, 89 | .textbox-disabled .spinner-button-right:hover, 90 | .textbox-icon-disabled .spinner-arrow-up:hover, 91 | .textbox-icon-disabled .spinner-arrow-down:hover { 92 | opacity: 0.6; 93 | filter: alpha(opacity=60); 94 | background-color: #E0ECFF; 95 | cursor: default; 96 | } 97 | .spinner .textbox-icon-disabled { 98 | opacity: 0.6; 99 | filter: alpha(opacity=60); 100 | } 101 | .spinner-arrow-up { 102 | background: url('images/spinner_arrows.png') no-repeat 1px center; 103 | background-color: #E0ECFF; 104 | } 105 | .spinner-arrow-down { 106 | background: url('images/spinner_arrows.png') no-repeat -15px center; 107 | background-color: #E0ECFF; 108 | } 109 | .spinner-button-up { 110 | background: url('images/spinner_arrows.png') no-repeat -32px center; 111 | } 112 | .spinner-button-down { 113 | background: url('images/spinner_arrows.png') no-repeat -48px center; 114 | } 115 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/textbox.css: -------------------------------------------------------------------------------- 1 | .textbox { 2 | position: relative; 3 | border: 1px solid #95B8E7; 4 | background-color: #fff; 5 | vertical-align: middle; 6 | display: inline-block; 7 | overflow: hidden; 8 | white-space: nowrap; 9 | margin: 0; 10 | padding: 0; 11 | -moz-border-radius: 5px 5px 5px 5px; 12 | -webkit-border-radius: 5px 5px 5px 5px; 13 | border-radius: 5px 5px 5px 5px; 14 | } 15 | .textbox .textbox-text { 16 | font-size: 14px; 17 | border: 0; 18 | margin: 0; 19 | padding: 0 4px; 20 | white-space: normal; 21 | vertical-align: top; 22 | outline-style: none; 23 | resize: none; 24 | -moz-border-radius: 5px 5px 5px 5px; 25 | -webkit-border-radius: 5px 5px 5px 5px; 26 | border-radius: 5px 5px 5px 5px; 27 | height: 28px; 28 | line-height: 28px; 29 | } 30 | .textbox textarea.textbox-text { 31 | line-height: normal; 32 | } 33 | .textbox .textbox-text::-ms-clear, 34 | .textbox .textbox-text::-ms-reveal { 35 | display: none; 36 | } 37 | .textbox textarea.textbox-text { 38 | white-space: pre-wrap; 39 | } 40 | .textbox .textbox-prompt { 41 | font-size: 14px; 42 | color: #aaa; 43 | } 44 | .textbox .textbox-bgicon { 45 | background-position: 3px center; 46 | padding-left: 21px; 47 | } 48 | .textbox .textbox-button, 49 | .textbox .textbox-button:hover { 50 | position: absolute; 51 | top: 0; 52 | padding: 0; 53 | vertical-align: top; 54 | -moz-border-radius: 0 0 0 0; 55 | -webkit-border-radius: 0 0 0 0; 56 | border-radius: 0 0 0 0; 57 | } 58 | .textbox .textbox-button-right, 59 | .textbox .textbox-button-right:hover { 60 | right: 0; 61 | border-width: 0 0 0 1px; 62 | } 63 | .textbox .textbox-button-left, 64 | .textbox .textbox-button-left:hover { 65 | left: 0; 66 | border-width: 0 1px 0 0; 67 | } 68 | .textbox .textbox-button-top, 69 | .textbox .textbox-button-top:hover { 70 | left: 0; 71 | border-width: 0 0 1px 0; 72 | } 73 | .textbox .textbox-button-bottom, 74 | .textbox .textbox-button-bottom:hover { 75 | top: auto; 76 | bottom: 0; 77 | left: 0; 78 | border-width: 1px 0 0 0; 79 | } 80 | .textbox-addon { 81 | position: absolute; 82 | top: 0; 83 | } 84 | .textbox-label { 85 | display: inline-block; 86 | width: 80px; 87 | height: 30px; 88 | line-height: 30px; 89 | vertical-align: middle; 90 | overflow: hidden; 91 | text-overflow: ellipsis; 92 | white-space: nowrap; 93 | margin: 0; 94 | padding-right: 5px; 95 | } 96 | .textbox-label-after { 97 | padding-left: 5px; 98 | padding-right: 0; 99 | } 100 | .textbox-label-top { 101 | display: block; 102 | width: auto; 103 | padding: 0; 104 | } 105 | .textbox-disabled, 106 | .textbox-label-disabled { 107 | opacity: 0.6; 108 | filter: alpha(opacity=60); 109 | } 110 | .textbox-icon { 111 | display: inline-block; 112 | width: 18px; 113 | height: 20px; 114 | overflow: hidden; 115 | vertical-align: top; 116 | background-position: center center; 117 | cursor: pointer; 118 | opacity: 0.6; 119 | filter: alpha(opacity=60); 120 | text-decoration: none; 121 | outline-style: none; 122 | } 123 | .textbox-icon-disabled, 124 | .textbox-icon-readonly { 125 | cursor: default; 126 | } 127 | .textbox-icon:hover { 128 | opacity: 1.0; 129 | filter: alpha(opacity=100); 130 | } 131 | .textbox-icon-disabled:hover { 132 | opacity: 0.6; 133 | filter: alpha(opacity=60); 134 | } 135 | .textbox-focused { 136 | border-color: #6b9cde; 137 | -moz-box-shadow: 0 0 3px 0 #95B8E7; 138 | -webkit-box-shadow: 0 0 3px 0 #95B8E7; 139 | box-shadow: 0 0 3px 0 #95B8E7; 140 | } 141 | .textbox-invalid { 142 | border-color: #ffa8a8; 143 | background-color: #fff3f3; 144 | } 145 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/layout.css: -------------------------------------------------------------------------------- 1 | .layout { 2 | position: relative; 3 | overflow: hidden; 4 | margin: 0; 5 | padding: 0; 6 | z-index: 0; 7 | } 8 | .layout-panel { 9 | position: absolute; 10 | overflow: hidden; 11 | } 12 | .layout-body { 13 | min-width: 1px; 14 | min-height: 1px; 15 | } 16 | .layout-panel-east, 17 | .layout-panel-west { 18 | z-index: 2; 19 | } 20 | .layout-panel-north, 21 | .layout-panel-south { 22 | z-index: 3; 23 | } 24 | .layout-expand { 25 | position: absolute; 26 | padding: 0px; 27 | font-size: 1px; 28 | cursor: pointer; 29 | z-index: 1; 30 | } 31 | .layout-expand .panel-header, 32 | .layout-expand .panel-body { 33 | background: transparent; 34 | filter: none; 35 | overflow: hidden; 36 | } 37 | .layout-expand .panel-header { 38 | border-bottom-width: 0px; 39 | } 40 | .layout-expand .panel-body { 41 | position: relative; 42 | } 43 | .layout-expand .panel-body .panel-icon { 44 | margin-top: 0; 45 | top: 0; 46 | left: 50%; 47 | margin-left: -8px; 48 | } 49 | .layout-expand-west .panel-header .panel-icon, 50 | .layout-expand-east .panel-header .panel-icon { 51 | display: none; 52 | } 53 | .layout-expand-title { 54 | position: absolute; 55 | top: 0; 56 | left: 21px; 57 | white-space: nowrap; 58 | word-wrap: normal; 59 | -webkit-transform: rotate(90deg); 60 | -webkit-transform-origin: 0 0; 61 | -moz-transform: rotate(90deg); 62 | -moz-transform-origin: 0 0; 63 | -o-transform: rotate(90deg); 64 | -o-transform-origin: 0 0; 65 | transform: rotate(90deg); 66 | transform-origin: 0 0; 67 | } 68 | .layout-expand-title-up { 69 | position: absolute; 70 | top: 0; 71 | left: 0; 72 | text-align: right; 73 | padding-left: 5px; 74 | white-space: nowrap; 75 | word-wrap: normal; 76 | -webkit-transform: rotate(-90deg); 77 | -webkit-transform-origin: 0 0; 78 | -moz-transform: rotate(-90deg); 79 | -moz-transform-origin: 0 0; 80 | -o-transform: rotate(-90deg); 81 | -o-transform-origin: 0 0; 82 | transform: rotate(-90deg); 83 | transform-origin: 0 0; 84 | } 85 | .layout-expand-with-icon { 86 | top: 18px; 87 | } 88 | .layout-expand .panel-body-noheader .layout-expand-title, 89 | .layout-expand .panel-body-noheader .panel-icon { 90 | top: 5px; 91 | } 92 | .layout-expand .panel-body-noheader .layout-expand-with-icon { 93 | top: 23px; 94 | } 95 | .layout-split-proxy-h, 96 | .layout-split-proxy-v { 97 | position: absolute; 98 | font-size: 1px; 99 | display: none; 100 | z-index: 5; 101 | } 102 | .layout-split-proxy-h { 103 | width: 5px; 104 | cursor: e-resize; 105 | } 106 | .layout-split-proxy-v { 107 | height: 5px; 108 | cursor: n-resize; 109 | } 110 | .layout-mask { 111 | position: absolute; 112 | background: #fafafa; 113 | filter: alpha(opacity=10); 114 | opacity: 0.10; 115 | z-index: 4; 116 | } 117 | .layout-button-up { 118 | background: url('images/layout_arrows.png') no-repeat -16px -16px; 119 | } 120 | .layout-button-down { 121 | background: url('images/layout_arrows.png') no-repeat -16px 0; 122 | } 123 | .layout-button-left { 124 | background: url('images/layout_arrows.png') no-repeat 0 0; 125 | } 126 | .layout-button-right { 127 | background: url('images/layout_arrows.png') no-repeat 0 -16px; 128 | } 129 | .layout-split-proxy-h, 130 | .layout-split-proxy-v { 131 | background-color: #aac5e7; 132 | } 133 | .layout-split-north { 134 | border-bottom: 5px solid #E6EEF8; 135 | } 136 | .layout-split-south { 137 | border-top: 5px solid #E6EEF8; 138 | } 139 | .layout-split-east { 140 | border-left: 5px solid #E6EEF8; 141 | } 142 | .layout-split-west { 143 | border-right: 5px solid #E6EEF8; 144 | } 145 | .layout-expand { 146 | background-color: #E0ECFF; 147 | } 148 | .layout-expand-over { 149 | background-color: #E0ECFF; 150 | } 151 | -------------------------------------------------------------------------------- /parser/SmokeDetectorParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class SmokeDetectorParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'SmokeDetector_SmokeSensor': SmokeDetectorSmokeSensorParser 12 | } 13 | } 14 | } 15 | SmokeDetectorParser.modelName = ['smoke', 'sensor_smoke']; 16 | module.exports = SmokeDetectorParser; 17 | 18 | class SmokeDetectorSmokeSensorParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Smoke Detector', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.SmokeSensor(accessoryName); 40 | service.getCharacteristic(that.Characteristic.SmokeDetected); 41 | result.push(service); 42 | 43 | var batteryService = new that.Service.BatteryService(accessoryName); 44 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 45 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 46 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 47 | result.push(batteryService); 48 | 49 | return result; 50 | } 51 | 52 | parserAccessories(jsonObj) { 53 | var that = this; 54 | var deviceSid = jsonObj['sid']; 55 | var uuid = that.getAccessoryUUID(deviceSid); 56 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 57 | if(accessory) { 58 | var service = accessory.getService(that.Service.SmokeSensor); 59 | var smokeDetectedCharacteristic = service.getCharacteristic(that.Characteristic.SmokeDetected); 60 | var value = that.getSmokeDetectedCharacteristicValue(jsonObj, null); 61 | if(null != value) { 62 | smokeDetectedCharacteristic.updateValue(value ? that.Characteristic.SmokeDetected.SMOKE_DETECTED : that.Characteristic.SmokeDetected.SMOKE_NOT_DETECTED); 63 | } 64 | 65 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 66 | if (smokeDetectedCharacteristic.listeners('get').length == 0) { 67 | smokeDetectedCharacteristic.on("get", function(callback) { 68 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 69 | that.platform.sendReadCommand(deviceSid, command).then(result => { 70 | var value = that.getSmokeDetectedCharacteristicValue(result, null); 71 | if(null != value) { 72 | callback(null, value ? that.Characteristic.SmokeDetected.SMOKE_DETECTED : that.Characteristic.SmokeDetected.SMOKE_NOT_DETECTED); 73 | } else { 74 | callback(new Error('get value fail: ' + result)); 75 | } 76 | }).catch(function(err) { 77 | that.platform.log.error(err); 78 | callback(err); 79 | }); 80 | }); 81 | } 82 | } 83 | 84 | that.parserBatteryService(accessory, jsonObj); 85 | } 86 | } 87 | 88 | getSmokeDetectedCharacteristicValue(jsonObj, defaultValue) { 89 | var value = this.getValueFrJsonObjData(jsonObj, 'alarm'); 90 | if(value === '1' || value === '2') { 91 | return true; 92 | } else if(value === '0') { 93 | return false; 94 | } else { 95 | return false; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /parser/NatgasDetectorParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class NatgasDetectorParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'NatgasDetector_SmokeSensor': NatgasDetectorSmokeSensorParser 12 | } 13 | } 14 | } 15 | NatgasDetectorParser.modelName = ['natgas', 'sensor_natgas']; 16 | module.exports = NatgasDetectorParser; 17 | 18 | class NatgasDetectorSmokeSensorParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Natgas Detector', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.SmokeSensor(accessoryName); 40 | service.getCharacteristic(that.Characteristic.SmokeDetected); 41 | result.push(service); 42 | 43 | var batteryService = new that.Service.BatteryService(accessoryName); 44 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 45 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 46 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 47 | result.push(batteryService); 48 | 49 | return result; 50 | } 51 | 52 | parserAccessories(jsonObj) { 53 | var that = this; 54 | var deviceSid = jsonObj['sid']; 55 | var uuid = that.getAccessoryUUID(deviceSid); 56 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 57 | if(accessory) { 58 | var service = accessory.getService(that.Service.SmokeSensor); 59 | var smokeDetectedCharacteristic = service.getCharacteristic(that.Characteristic.SmokeDetected); 60 | var value = that.getSmokeDetectedCharacteristicValue(jsonObj, null); 61 | if(null != value) { 62 | smokeDetectedCharacteristic.updateValue(value ? that.Characteristic.SmokeDetected.SMOKE_DETECTED : that.Characteristic.SmokeDetected.SMOKE_NOT_DETECTED); 63 | } 64 | 65 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 66 | if (smokeDetectedCharacteristic.listeners('get').length == 0) { 67 | smokeDetectedCharacteristic.on("get", function(callback) { 68 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 69 | that.platform.sendReadCommand(deviceSid, command).then(result => { 70 | var value = that.getSmokeDetectedCharacteristicValue(result, null); 71 | if(null != value) { 72 | callback(null, value ? that.Characteristic.SmokeDetected.SMOKE_DETECTED : that.Characteristic.SmokeDetected.SMOKE_NOT_DETECTED); 73 | } else { 74 | callback(new Error('get value fail: ' + result)); 75 | } 76 | }).catch(function(err) { 77 | that.platform.log.error(err); 78 | callback(err); 79 | }); 80 | }); 81 | } 82 | } 83 | 84 | that.parserBatteryService(accessory, jsonObj); 85 | } 86 | } 87 | 88 | getSmokeDetectedCharacteristicValue(jsonObj, defaultValue) { 89 | var value = this.getValueFrJsonObjData(jsonObj, 'alarm'); 90 | if(value === '1' || value === '2') { 91 | return true; 92 | } else if(value === '0') { 93 | return false; 94 | } else { 95 | return false; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /parser/MotionSensorParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class MotionSensorParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'MotionSensor_MotionSensor': MotionSensorMotionSensorParser 12 | } 13 | } 14 | } 15 | MotionSensorParser.modelName = ['motion']; 16 | module.exports = MotionSensorParser; 17 | 18 | class MotionSensorMotionSensorParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Motion Sensor', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.MotionSensor(accessoryName); 40 | service.getCharacteristic(that.Characteristic.MotionDetected); 41 | result.push(service); 42 | 43 | var batteryService = new that.Service.BatteryService(accessoryName); 44 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 45 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 46 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 47 | result.push(batteryService); 48 | 49 | return result; 50 | } 51 | 52 | parserAccessories(jsonObj) { 53 | var that = this; 54 | var deviceSid = jsonObj['sid']; 55 | var uuid = that.getAccessoryUUID(deviceSid); 56 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 57 | if(accessory) { 58 | var service = accessory.getService(that.Service.MotionSensor); 59 | var motionDetectedCharacteristic = service.getCharacteristic(that.Characteristic.MotionDetected); 60 | var value = that.getMotionDetectedCharacteristicValue(jsonObj, null); 61 | if(null != value) { 62 | motionDetectedCharacteristic.updateValue(value); 63 | } 64 | 65 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 66 | if (motionDetectedCharacteristic.listeners('get').length == 0) { 67 | motionDetectedCharacteristic.on("get", function(callback) { 68 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 69 | that.platform.sendReadCommand(deviceSid, command).then(result => { 70 | var value = that.getMotionDetectedCharacteristicValue(result, null); 71 | if(null != value) { 72 | callback(null, value); 73 | } else { 74 | callback(new Error('get value fail: ' + result)); 75 | } 76 | }).catch(function(err) { 77 | that.platform.log.error(err); 78 | callback(err); 79 | }); 80 | }); 81 | } 82 | } 83 | 84 | that.parserBatteryService(accessory, jsonObj); 85 | } 86 | } 87 | 88 | getMotionDetectedCharacteristicValue(jsonObj, defaultValue) { 89 | var value = null; 90 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 91 | if(1 == proto_version_prefix) { 92 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 93 | } else if(2 == proto_version_prefix) { 94 | value = this.getValueFrJsonObjData2(jsonObj, 'motion_status'); 95 | } else { 96 | } 97 | 98 | return (null != value) ? (value === 'motion') : false; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/tree.css: -------------------------------------------------------------------------------- 1 | .tree { 2 | margin: 0; 3 | padding: 0; 4 | list-style-type: none; 5 | } 6 | .tree li { 7 | white-space: nowrap; 8 | } 9 | .tree li ul { 10 | list-style-type: none; 11 | margin: 0; 12 | padding: 0; 13 | } 14 | .tree-node { 15 | height: 26px; 16 | white-space: nowrap; 17 | cursor: pointer; 18 | } 19 | .tree-hit { 20 | cursor: pointer; 21 | } 22 | .tree-expanded, 23 | .tree-collapsed, 24 | .tree-folder, 25 | .tree-file, 26 | .tree-checkbox, 27 | .tree-indent { 28 | display: inline-block; 29 | width: 16px; 30 | height: 18px; 31 | margin: 4px 0; 32 | vertical-align: middle; 33 | overflow: hidden; 34 | } 35 | .tree-expanded { 36 | background: url('images/tree_icons.png') no-repeat -18px 0px; 37 | } 38 | .tree-expanded-hover { 39 | background: url('images/tree_icons.png') no-repeat -50px 0px; 40 | } 41 | .tree-collapsed { 42 | background: url('images/tree_icons.png') no-repeat 0px 0px; 43 | } 44 | .tree-collapsed-hover { 45 | background: url('images/tree_icons.png') no-repeat -32px 0px; 46 | } 47 | .tree-lines .tree-expanded, 48 | .tree-lines .tree-root-first .tree-expanded { 49 | background: url('images/tree_icons.png') no-repeat -144px 0; 50 | } 51 | .tree-lines .tree-collapsed, 52 | .tree-lines .tree-root-first .tree-collapsed { 53 | background: url('images/tree_icons.png') no-repeat -128px 0; 54 | } 55 | .tree-lines .tree-node-last .tree-expanded, 56 | .tree-lines .tree-root-one .tree-expanded { 57 | background: url('images/tree_icons.png') no-repeat -80px 0; 58 | } 59 | .tree-lines .tree-node-last .tree-collapsed, 60 | .tree-lines .tree-root-one .tree-collapsed { 61 | background: url('images/tree_icons.png') no-repeat -64px 0; 62 | } 63 | .tree-line { 64 | background: url('images/tree_icons.png') no-repeat -176px 0; 65 | } 66 | .tree-join { 67 | background: url('images/tree_icons.png') no-repeat -192px 0; 68 | } 69 | .tree-joinbottom { 70 | background: url('images/tree_icons.png') no-repeat -160px 0; 71 | } 72 | .tree-folder { 73 | background: url('images/tree_icons.png') no-repeat -208px 0; 74 | } 75 | .tree-folder-open { 76 | background: url('images/tree_icons.png') no-repeat -224px 0; 77 | } 78 | .tree-file { 79 | background: url('images/tree_icons.png') no-repeat -240px 0; 80 | } 81 | .tree-loading { 82 | background: url('images/loading.gif') no-repeat center center; 83 | } 84 | .tree-checkbox0 { 85 | background: url('images/tree_icons.png') no-repeat -208px -18px; 86 | } 87 | .tree-checkbox1 { 88 | background: url('images/tree_icons.png') no-repeat -224px -18px; 89 | } 90 | .tree-checkbox2 { 91 | background: url('images/tree_icons.png') no-repeat -240px -18px; 92 | } 93 | .tree-title { 94 | font-size: 14px; 95 | display: inline-block; 96 | text-decoration: none; 97 | vertical-align: middle; 98 | white-space: nowrap; 99 | padding: 0 2px; 100 | margin: 4px 0; 101 | height: 18px; 102 | line-height: 18px; 103 | } 104 | .tree-node-proxy { 105 | font-size: 14px; 106 | line-height: 20px; 107 | padding: 0 2px 0 20px; 108 | border-width: 1px; 109 | border-style: solid; 110 | z-index: 9900000; 111 | } 112 | .tree-dnd-icon { 113 | display: inline-block; 114 | position: absolute; 115 | width: 16px; 116 | height: 18px; 117 | left: 2px; 118 | top: 50%; 119 | margin-top: -9px; 120 | } 121 | .tree-dnd-yes { 122 | background: url('images/tree_icons.png') no-repeat -256px 0; 123 | } 124 | .tree-dnd-no { 125 | background: url('images/tree_icons.png') no-repeat -256px -18px; 126 | } 127 | .tree-node-top { 128 | border-top: 1px dotted red; 129 | } 130 | .tree-node-bottom { 131 | border-bottom: 1px dotted red; 132 | } 133 | .tree-node-append .tree-title { 134 | border: 1px dotted red; 135 | } 136 | .tree-editor { 137 | border: 1px solid #95B8E7; 138 | font-size: 14px; 139 | height: 26px; 140 | line-height: 26px; 141 | padding: 0 4px; 142 | margin: 0; 143 | width: 80px; 144 | outline-style: none; 145 | vertical-align: middle; 146 | position: absolute; 147 | top: 0; 148 | } 149 | .tree-node-proxy { 150 | background-color: #ffffff; 151 | color: #000000; 152 | border-color: #95B8E7; 153 | } 154 | .tree-node-hover { 155 | background: #eaf2ff; 156 | color: #000000; 157 | } 158 | .tree-node-selected { 159 | background: #ffe48d; 160 | color: #000000; 161 | } 162 | .tree-node-hidden { 163 | display: none; 164 | } 165 | -------------------------------------------------------------------------------- /lib/ParseUtil.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var path = require('path'); 3 | 4 | class ParseUtil { 5 | constructor(platform) { 6 | this.platform = platform; 7 | this.parsers = {}; 8 | this.gatewayModels = []; 9 | 10 | this.loadParser(); 11 | } 12 | 13 | loadParser() { 14 | var that = this; 15 | var parsersPath = path.resolve(__dirname, './../parser/'); 16 | that.platform.log.debug('loading parsers from: ' + parsersPath); 17 | fs.readdir(parsersPath, function (err, files) { 18 | if (err) { 19 | return; 20 | } 21 | files.forEach(function (filename) { 22 | if(filename == null || filename == '') { 23 | return; 24 | } 25 | if('.js' != filename.substring(filename.lastIndexOf('.'), filename.length)) { 26 | return; 27 | } 28 | if('AccessoryParser.js' == filename) { 29 | return; 30 | } 31 | if('DeviceParser.js' == filename) { 32 | return; 33 | } 34 | // that.platform.log.debug(filename); 35 | 36 | var parserPath = path.join(parsersPath, filename); 37 | try { 38 | var parserFile = require(parserPath); 39 | var parserModel = parserFile && parserFile.modelName; 40 | if (!parserModel) { 41 | return; 42 | } 43 | 44 | var parser = new parserFile(that.platform); 45 | if (parserModel instanceof Array) { 46 | parserModel.forEach(function (model) { 47 | that.parsers[model] = parser; 48 | if(parserFile.isGateway) { 49 | that.gatewayModels.push(model); 50 | } 51 | // that.platform.log.debug(model); 52 | }); 53 | } else { 54 | that.parsers[parserModel] = parser; 55 | if(parserFile.isGateway) { 56 | that.gatewayModels.push(parserModel); 57 | } 58 | // that.platform.log.debug(parserModel); 59 | } 60 | } catch (error) { 61 | that.platform.log.error(error); 62 | } 63 | }); 64 | }); 65 | } 66 | 67 | isGatewayModel(modelName) { 68 | if (this.gatewayModels.indexOf(modelName) > -1) { 69 | return true; 70 | } else { 71 | return false; 72 | } 73 | } 74 | 75 | getByModel(model) { 76 | return (model in this.parsers) ? this.parsers[model]: null; 77 | } 78 | 79 | getByModelName(model) { 80 | function getFnName(fn){ 81 | return typeof fn !== "function" ? 82 | undefined: 83 | fn.name || 84 | /function (.+?)\(/.exec(fn + "")[1]; 85 | } 86 | var parse = this.getByModel(model); 87 | if(parse) { 88 | return getFnName(parse.constructor).replace("Parser", ""); 89 | } else { 90 | return ""; 91 | } 92 | } 93 | 94 | getCreateAccessories(jsonObj) { 95 | var result = []; 96 | 97 | var model = jsonObj['model']; 98 | var parser = this.getByModel(model); 99 | if(parser) { 100 | result = parser.getCreateAccessories(jsonObj); 101 | } 102 | 103 | return result; 104 | } 105 | 106 | parserAccessories(jsonObj) { 107 | var result = []; 108 | 109 | var model = jsonObj['model']; 110 | var parser = this.getByModel(model); 111 | if(parser) { 112 | result = parser.parserAccessories(jsonObj); 113 | } 114 | 115 | return result; 116 | } 117 | 118 | getAccessoriesUUID(sid, deviceModel) { 119 | var result = []; 120 | 121 | var parser = this.getByModel(deviceModel); 122 | if(parser) { 123 | result = parser.getAccessoriesUUID(sid); 124 | } 125 | 126 | return result; 127 | } 128 | } 129 | 130 | module.exports = ParseUtil; -------------------------------------------------------------------------------- /parser/ContactSensorParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class ContactSensorParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'ContactSensor_ContactSensor': ContactSensorContactSensorParser 12 | } 13 | } 14 | } 15 | ContactSensorParser.modelName = ['magnet', 'sensor_magnet']; 16 | module.exports = ContactSensorParser; 17 | 18 | class ContactSensorContactSensorParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Contact Sensor', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.ContactSensor(accessoryName); 40 | service.getCharacteristic(that.Characteristic.ContactSensorState); 41 | result.push(service); 42 | 43 | var batteryService = new that.Service.BatteryService(accessoryName); 44 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 45 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 46 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 47 | result.push(batteryService); 48 | 49 | return result; 50 | } 51 | 52 | parserAccessories(jsonObj) { 53 | var that = this; 54 | var deviceSid = jsonObj['sid']; 55 | var uuid = that.getAccessoryUUID(deviceSid); 56 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 57 | if(accessory) { 58 | var service = accessory.getService(that.Service.ContactSensor); 59 | var contactSensorStateCharacteristic = service.getCharacteristic(that.Characteristic.ContactSensorState); 60 | var value = that.getContactSensorStateCharacteristicValue(jsonObj, null); 61 | if(null != value) { 62 | contactSensorStateCharacteristic.updateValue(value ? that.Characteristic.ContactSensorState.CONTACT_DETECTED : that.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED); 63 | } 64 | 65 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 66 | if (contactSensorStateCharacteristic.listeners('get').length == 0) { 67 | contactSensorStateCharacteristic.on("get", function(callback) { 68 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 69 | that.platform.sendReadCommand(deviceSid, command).then(result => { 70 | var value = that.getContactSensorStateCharacteristicValue(result, null); 71 | if(null != value) { 72 | callback(null, value ? that.Characteristic.ContactSensorState.CONTACT_DETECTED : that.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED); 73 | } else { 74 | callback(new Error('get value fail: ' + result)); 75 | } 76 | }).catch(function(err) { 77 | that.platform.log.error(err); 78 | callback(err); 79 | }); 80 | }); 81 | } 82 | } 83 | 84 | that.parserBatteryService(accessory, jsonObj); 85 | } 86 | } 87 | 88 | getContactSensorStateCharacteristicValue(jsonObj, defaultValue) { 89 | var value = null; 90 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 91 | if(1 == proto_version_prefix) { 92 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 93 | } else if(2 == proto_version_prefix) { 94 | value = this.getValueFrJsonObjData2(jsonObj, 'window_status'); 95 | } else { 96 | } 97 | 98 | return (null != value) ? (value === 'close') : defaultValue; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /parser/ContactSensor2Parser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class ContactSensor2Parser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'ContactSensor2_ContactSensor': ContactSensor2ContactSensorParser 12 | } 13 | } 14 | } 15 | ContactSensor2Parser.modelName = ['sensor_magnet.aq2']; 16 | module.exports = ContactSensor2Parser; 17 | 18 | class ContactSensor2ContactSensorParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Contact Sensor 2', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.ContactSensor(accessoryName); 40 | service.getCharacteristic(that.Characteristic.ContactSensorState); 41 | result.push(service); 42 | 43 | var batteryService = new that.Service.BatteryService(accessoryName); 44 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 45 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 46 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 47 | result.push(batteryService); 48 | 49 | return result; 50 | } 51 | 52 | parserAccessories(jsonObj) { 53 | var that = this; 54 | var deviceSid = jsonObj['sid']; 55 | var uuid = that.getAccessoryUUID(deviceSid); 56 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 57 | if(accessory) { 58 | var service = accessory.getService(that.Service.ContactSensor); 59 | var contactSensorStateCharacteristic = service.getCharacteristic(that.Characteristic.ContactSensorState); 60 | var value = that.getContactSensorStateCharacteristicValue(jsonObj, null); 61 | if(null != value) { 62 | contactSensorStateCharacteristic.updateValue(value ? that.Characteristic.ContactSensorState.CONTACT_DETECTED : that.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED); 63 | } 64 | 65 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 66 | if (contactSensorStateCharacteristic.listeners('get').length == 0) { 67 | contactSensorStateCharacteristic.on("get", function(callback) { 68 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 69 | that.platform.sendReadCommand(deviceSid, command).then(result => { 70 | var value = that.getContactSensorStateCharacteristicValue(result, null); 71 | if(null != value) { 72 | callback(null, value ? that.Characteristic.ContactSensorState.CONTACT_DETECTED : that.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED); 73 | } else { 74 | callback(new Error('get value fail: ' + result)); 75 | } 76 | }).catch(function(err) { 77 | that.platform.log.error(err); 78 | callback(err); 79 | }); 80 | }); 81 | } 82 | } 83 | 84 | that.parserBatteryService(accessory, jsonObj); 85 | } 86 | } 87 | 88 | getContactSensorStateCharacteristicValue(jsonObj, defaultValue) { 89 | var value = null; 90 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 91 | if(1 == proto_version_prefix) { 92 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 93 | } else if(2 == proto_version_prefix) { 94 | value = this.getValueFrJsonObjData2(jsonObj, 'window_status'); 95 | } else { 96 | } 97 | 98 | return (null != value) ? (value === 'close') : defaultValue; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /parser/WaterDetectorParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class WaterDetectorParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'WaterDetector_LeakSensor': WaterDetectorLeakSensorParser 12 | } 13 | } 14 | } 15 | WaterDetectorParser.modelName = ['sensor_wleak.aq1']; 16 | module.exports = WaterDetectorParser; 17 | 18 | class WaterDetectorLeakSensorParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Water Detector', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.LeakSensor(accessoryName); 40 | service.getCharacteristic(that.Characteristic.LeakDetected); 41 | result.push(service); 42 | 43 | var batteryService = new that.Service.BatteryService(accessoryName); 44 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 45 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 46 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 47 | result.push(batteryService); 48 | 49 | return result; 50 | } 51 | 52 | parserAccessories(jsonObj) { 53 | var that = this; 54 | var deviceSid = jsonObj['sid']; 55 | var uuid = that.getAccessoryUUID(deviceSid); 56 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 57 | if(accessory) { 58 | var service = accessory.getService(that.Service.LeakSensor); 59 | var leakDetectedCharacteristic = service.getCharacteristic(that.Characteristic.LeakDetected); 60 | var value = that.getLeakDetectedCharacteristicValue(jsonObj, null); 61 | if(null != value) { 62 | leakDetectedCharacteristic.updateValue(value ? that.Characteristic.LeakDetected.LEAK_DETECTED : that.Characteristic.LeakDetected.LEAK_NOT_DETECTED); 63 | } 64 | 65 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 66 | if (leakDetectedCharacteristic.listeners('get').length == 0) { 67 | leakDetectedCharacteristic.on("get", function(callback) { 68 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 69 | that.platform.sendReadCommand(deviceSid, command).then(result => { 70 | var value = that.getLeakDetectedCharacteristicValue(result, false); 71 | if(null != value) { 72 | callback(null, value ? that.Characteristic.LeakDetected.LEAK_DETECTED : that.Characteristic.LeakDetected.LEAK_NOT_DETECTED); 73 | } else { 74 | callback(new Error('get value fail: ' + result)); 75 | } 76 | }).catch(function(err) { 77 | that.platform.log.error(err); 78 | callback(err); 79 | }); 80 | }); 81 | } 82 | } 83 | 84 | that.parserBatteryService(accessory, jsonObj); 85 | } 86 | } 87 | 88 | getLeakDetectedCharacteristicValue(jsonObj, defaultValue) { 89 | var value = null; 90 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 91 | if(1 == proto_version_prefix) { 92 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 93 | } else if(2 == proto_version_prefix) { 94 | value = this.getValueFrJsonObjData2(jsonObj, 'wleak_status'); 95 | } else { 96 | } 97 | 98 | if(value === 'leak') { 99 | return true; 100 | } else if(value === 'no_leak') { 101 | return false; 102 | } else { 103 | return false; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /manage/css/device_icon.css: -------------------------------------------------------------------------------- 1 | .icon-device-Gateway { 2 | background:url('../images/deviceIcons/Gateway.png') no-repeat center center; 3 | background-size: 16px 16px; 4 | } 5 | 6 | .icon-device-ContactSensor { 7 | background:url('../images/deviceIcons/ContactSensor.png') no-repeat center center; 8 | background-size: 16px 16px; 9 | } 10 | 11 | .icon-device-MotionSensor { 12 | background:url('../images/deviceIcons/MotionSensor.png') no-repeat center center; 13 | background-size: 16px 16px; 14 | } 15 | 16 | .icon-device-Button { 17 | background:url('../images/deviceIcons/Button.png') no-repeat center center; 18 | background-size: 16px 16px; 19 | } 20 | 21 | .icon-device-TemperatureAndHumiditySensor { 22 | background:url('../images/deviceIcons/TemperatureAndHumiditySensor.png') no-repeat center center; 23 | background-size: 16px 16px; 24 | } 25 | 26 | .icon-device-SingleSwitch { 27 | background:url('../images/deviceIcons/SingleSwitch.png') no-repeat center center; 28 | background-size: 16px 16px; 29 | } 30 | 31 | .icon-device-DuplexSwitch { 32 | background:url('../images/deviceIcons/DuplexSwitch.png') no-repeat center center; 33 | background-size: 16px 16px; 34 | } 35 | 36 | .icon-device-SingleSwitchLN { 37 | background:url('../images/deviceIcons/SingleSwitchLN.png') no-repeat center center; 38 | background-size: 16px 16px; 39 | } 40 | 41 | .icon-device-DuplexSwitchLN { 42 | background:url('../images/deviceIcons/DuplexSwitchLN.png') no-repeat center center; 43 | background-size: 16px 16px; 44 | } 45 | 46 | .icon-device-SingleButton86 { 47 | background:url('../images/deviceIcons/SingleButton86.png') no-repeat center center; 48 | background-size: 16px 16px; 49 | } 50 | 51 | .icon-device-DuplexButton86 { 52 | background:url('../images/deviceIcons/DuplexButton86.png') no-repeat center center; 53 | background-size: 16px 16px; 54 | } 55 | 56 | .icon-device-PlugBase { 57 | background:url('../images/deviceIcons/PlugBase.png') no-repeat center center; 58 | background-size: 16px 16px; 59 | } 60 | 61 | .icon-device-PlugBase86 { 62 | background:url('../images/deviceIcons/PlugBase86.png') no-repeat center center; 63 | background-size: 16px 16px; 64 | } 65 | 66 | .icon-device-MagicSquare { 67 | background:url('../images/deviceIcons/MagicSquare.png') no-repeat center center; 68 | background-size: 16px 16px; 69 | } 70 | 71 | .icon-device-SmokeDetector { 72 | background:url('../images/deviceIcons/SmokeDetector.png') no-repeat center center; 73 | background-size: 16px 16px; 74 | } 75 | 76 | .icon-device-NatgasDetector { 77 | background:url('../images/deviceIcons/NatgasDetector.png') no-repeat center center; 78 | background-size: 16px 16px; 79 | } 80 | 81 | .icon-device-ElectricCurtain { 82 | background:url('../images/deviceIcons/ElectricCurtain.png') no-repeat center center; 83 | background-size: 16px 16px; 84 | } 85 | 86 | .icon-device-ContactSensor2 { 87 | background:url('../images/deviceIcons/ContactSensor2.png') no-repeat center center; 88 | background-size: 16px 16px; 89 | } 90 | 91 | .icon-device-MotionSensor2 { 92 | background:url('../images/deviceIcons/MotionSensor2.png') no-repeat center center; 93 | background-size: 16px 16px; 94 | } 95 | 96 | .icon-device-Button2 { 97 | background:url('../images/deviceIcons/Button2.png') no-repeat center center; 98 | background-size: 16px 16px; 99 | } 100 | 101 | .icon-device-TemperatureAndHumiditySensor2 { 102 | background:url('../images/deviceIcons/TemperatureAndHumiditySensor2.png') no-repeat center center; 103 | background-size: 16px 16px; 104 | } 105 | 106 | .icon-device-WaterDetector { 107 | background:url('../images/deviceIcons/WaterDetector.png') no-repeat center center; 108 | background-size: 16px 16px; 109 | } 110 | 111 | .icon-device-Lock { 112 | background:url('../images/deviceIcons/Lock.png') no-repeat center center; 113 | background-size: 16px 16px; 114 | } 115 | 116 | .icon-device-AcPartner { 117 | background:url('../images/deviceIcons/AcPartner.png') no-repeat center center; 118 | background-size: 16px 16px; 119 | } 120 | 121 | .icon-device-Button3 { 122 | background:url('../images/deviceIcons/Button3.png') no-repeat center center; 123 | background-size: 16px 16px; 124 | } 125 | 126 | .icon-device-DuplexButton862 { 127 | background:url('../images/deviceIcons/DuplexButton862.png') no-repeat center center; 128 | background-size: 16px 16px; 129 | } 130 | 131 | .icon-device-VibrationSensor { 132 | background:url('../images/deviceIcons/Vibration.png') no-repeat center center; 133 | background-size: 16px 16px; 134 | } -------------------------------------------------------------------------------- /manage/easyui/themes/default/calendar.css: -------------------------------------------------------------------------------- 1 | .calendar { 2 | border-width: 1px; 3 | border-style: solid; 4 | padding: 1px; 5 | overflow: hidden; 6 | } 7 | .calendar table { 8 | table-layout: fixed; 9 | border-collapse: separate; 10 | font-size: 14px; 11 | width: 100%; 12 | height: 100%; 13 | } 14 | .calendar table td, 15 | .calendar table th { 16 | font-size: 14px; 17 | } 18 | .calendar-noborder { 19 | border: 0; 20 | } 21 | .calendar-header { 22 | position: relative; 23 | height: 28px; 24 | } 25 | .calendar-title { 26 | text-align: center; 27 | height: 28px; 28 | } 29 | .calendar-title span { 30 | position: relative; 31 | display: inline-block; 32 | top: 0px; 33 | padding: 0 3px; 34 | height: 28px; 35 | line-height: 28px; 36 | font-size: 14px; 37 | cursor: pointer; 38 | -moz-border-radius: 5px 5px 5px 5px; 39 | -webkit-border-radius: 5px 5px 5px 5px; 40 | border-radius: 5px 5px 5px 5px; 41 | } 42 | .calendar-prevmonth, 43 | .calendar-nextmonth, 44 | .calendar-prevyear, 45 | .calendar-nextyear { 46 | position: absolute; 47 | top: 50%; 48 | margin-top: -8px; 49 | width: 16px; 50 | height: 16px; 51 | cursor: pointer; 52 | font-size: 1px; 53 | -moz-border-radius: 5px 5px 5px 5px; 54 | -webkit-border-radius: 5px 5px 5px 5px; 55 | border-radius: 5px 5px 5px 5px; 56 | } 57 | .calendar-prevmonth { 58 | left: 20px; 59 | background: url('images/calendar_arrows.png') no-repeat -16px 0; 60 | } 61 | .calendar-nextmonth { 62 | right: 20px; 63 | background: url('images/calendar_arrows.png') no-repeat -32px 0; 64 | } 65 | .calendar-prevyear { 66 | left: 3px; 67 | background: url('images/calendar_arrows.png') no-repeat 0px 0; 68 | } 69 | .calendar-nextyear { 70 | right: 3px; 71 | background: url('images/calendar_arrows.png') no-repeat -48px 0; 72 | } 73 | .calendar-body { 74 | position: relative; 75 | } 76 | .calendar-body th, 77 | .calendar-body td { 78 | text-align: center; 79 | } 80 | .calendar-day { 81 | border: 0; 82 | padding: 1px; 83 | cursor: pointer; 84 | -moz-border-radius: 5px 5px 5px 5px; 85 | -webkit-border-radius: 5px 5px 5px 5px; 86 | border-radius: 5px 5px 5px 5px; 87 | } 88 | .calendar-other-month { 89 | opacity: 0.3; 90 | filter: alpha(opacity=30); 91 | } 92 | .calendar-disabled { 93 | opacity: 0.6; 94 | filter: alpha(opacity=60); 95 | cursor: default; 96 | } 97 | .calendar-menu { 98 | position: absolute; 99 | top: 0; 100 | left: 0; 101 | width: 180px; 102 | height: 150px; 103 | padding: 5px; 104 | font-size: 14px; 105 | display: none; 106 | overflow: hidden; 107 | } 108 | .calendar-menu-year-inner { 109 | text-align: center; 110 | padding-bottom: 5px; 111 | } 112 | .calendar-menu-year { 113 | width: 80px; 114 | line-height: 26px; 115 | text-align: center; 116 | border-width: 1px; 117 | border-style: solid; 118 | outline-style: none; 119 | resize: none; 120 | margin: 0; 121 | padding: 0; 122 | font-weight: bold; 123 | font-size: 14px; 124 | -moz-border-radius: 5px 5px 5px 5px; 125 | -webkit-border-radius: 5px 5px 5px 5px; 126 | border-radius: 5px 5px 5px 5px; 127 | } 128 | .calendar-menu-prev, 129 | .calendar-menu-next { 130 | display: inline-block; 131 | width: 25px; 132 | height: 28px; 133 | vertical-align: top; 134 | cursor: pointer; 135 | -moz-border-radius: 5px 5px 5px 5px; 136 | -webkit-border-radius: 5px 5px 5px 5px; 137 | border-radius: 5px 5px 5px 5px; 138 | } 139 | .calendar-menu-prev { 140 | margin-right: 10px; 141 | background: url('images/calendar_arrows.png') no-repeat 5px center; 142 | } 143 | .calendar-menu-next { 144 | margin-left: 10px; 145 | background: url('images/calendar_arrows.png') no-repeat -44px center; 146 | } 147 | .calendar-menu-month { 148 | text-align: center; 149 | cursor: pointer; 150 | font-weight: bold; 151 | -moz-border-radius: 5px 5px 5px 5px; 152 | -webkit-border-radius: 5px 5px 5px 5px; 153 | border-radius: 5px 5px 5px 5px; 154 | } 155 | .calendar-body th, 156 | .calendar-menu-month { 157 | color: #4d4d4d; 158 | } 159 | .calendar-day { 160 | color: #000000; 161 | } 162 | .calendar-sunday { 163 | color: #CC2222; 164 | } 165 | .calendar-saturday { 166 | color: #00ee00; 167 | } 168 | .calendar-today { 169 | color: #0000ff; 170 | } 171 | .calendar-menu-year { 172 | border-color: #95B8E7; 173 | } 174 | .calendar { 175 | border-color: #95B8E7; 176 | } 177 | .calendar-header { 178 | background: #E0ECFF; 179 | } 180 | .calendar-body, 181 | .calendar-menu { 182 | background: #ffffff; 183 | } 184 | .calendar-body th { 185 | background: #F4F4F4; 186 | padding: 4px 0; 187 | } 188 | .calendar-hover, 189 | .calendar-nav-hover, 190 | .calendar-menu-hover { 191 | background-color: #eaf2ff; 192 | color: #000000; 193 | } 194 | .calendar-hover { 195 | border: 1px solid #b7d2ff; 196 | padding: 0; 197 | } 198 | .calendar-selected { 199 | background-color: #ffe48d; 200 | color: #000000; 201 | border: 1px solid #ffab3f; 202 | padding: 0; 203 | } 204 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/window.css: -------------------------------------------------------------------------------- 1 | .window { 2 | overflow: hidden; 3 | padding: 5px; 4 | border-width: 1px; 5 | border-style: solid; 6 | } 7 | .window .window-header { 8 | background: transparent; 9 | padding: 0px 0px 6px 0px; 10 | } 11 | .window .window-body { 12 | border-width: 1px; 13 | border-style: solid; 14 | border-top-width: 0px; 15 | } 16 | .window .window-body-noheader { 17 | border-top-width: 1px; 18 | } 19 | .window .panel-body-nobottom { 20 | border-bottom-width: 0; 21 | } 22 | .window .window-header .panel-icon, 23 | .window .window-header .panel-tool { 24 | top: 50%; 25 | margin-top: -11px; 26 | } 27 | .window .window-header .panel-icon { 28 | left: 1px; 29 | } 30 | .window .window-header .panel-tool { 31 | right: 1px; 32 | } 33 | .window .window-header .panel-with-icon { 34 | padding-left: 18px; 35 | } 36 | .window-proxy { 37 | position: absolute; 38 | overflow: hidden; 39 | } 40 | .window-proxy-mask { 41 | position: absolute; 42 | filter: alpha(opacity=5); 43 | opacity: 0.05; 44 | } 45 | .window-mask { 46 | position: absolute; 47 | left: 0; 48 | top: 0; 49 | width: 100%; 50 | height: 100%; 51 | filter: alpha(opacity=40); 52 | opacity: 0.40; 53 | font-size: 1px; 54 | overflow: hidden; 55 | } 56 | .window, 57 | .window-shadow { 58 | position: absolute; 59 | -moz-border-radius: 5px 5px 5px 5px; 60 | -webkit-border-radius: 5px 5px 5px 5px; 61 | border-radius: 5px 5px 5px 5px; 62 | } 63 | .window-shadow { 64 | background: #ccc; 65 | -moz-box-shadow: 2px 2px 3px #cccccc; 66 | -webkit-box-shadow: 2px 2px 3px #cccccc; 67 | box-shadow: 2px 2px 3px #cccccc; 68 | filter: progid:DXImageTransform.Microsoft.Blur(pixelRadius=2,MakeShadow=false,ShadowOpacity=0.2); 69 | } 70 | .window, 71 | .window .window-body { 72 | border-color: #95B8E7; 73 | } 74 | .window { 75 | background-color: #E0ECFF; 76 | background: -webkit-linear-gradient(top,#EFF5FF 0,#E0ECFF 20%); 77 | background: -moz-linear-gradient(top,#EFF5FF 0,#E0ECFF 20%); 78 | background: -o-linear-gradient(top,#EFF5FF 0,#E0ECFF 20%); 79 | background: linear-gradient(to bottom,#EFF5FF 0,#E0ECFF 20%); 80 | background-repeat: repeat-x; 81 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#EFF5FF,endColorstr=#E0ECFF,GradientType=0); 82 | } 83 | .window-proxy { 84 | border: 1px dashed #95B8E7; 85 | } 86 | .window-proxy-mask, 87 | .window-mask { 88 | background: #ccc; 89 | } 90 | .window .panel-footer { 91 | border: 1px solid #95B8E7; 92 | position: relative; 93 | top: -1px; 94 | } 95 | .window-thinborder { 96 | padding: 0; 97 | } 98 | .window-thinborder .window-header { 99 | padding: 5px 5px 6px 5px; 100 | } 101 | .window-thinborder .window-body { 102 | border-width: 0px; 103 | } 104 | .window-thinborder .window-footer { 105 | border-left: transparent; 106 | border-right: transparent; 107 | border-bottom: transparent; 108 | } 109 | .window-thinborder .window-header .panel-icon, 110 | .window-thinborder .window-header .panel-tool { 111 | margin-top: -9px; 112 | margin-left: 5px; 113 | margin-right: 5px; 114 | } 115 | .window-noborder { 116 | border: 0; 117 | } 118 | .window.panel-hleft .window-header { 119 | padding: 0 6px 0 0; 120 | } 121 | .window.panel-hright .window-header { 122 | padding: 0 0 0 6px; 123 | } 124 | .window.panel-hleft>.panel-header .panel-title { 125 | top: auto; 126 | left: 16px; 127 | } 128 | .window.panel-hright>.panel-header .panel-title { 129 | top: auto; 130 | right: 16px; 131 | } 132 | .window.panel-hleft>.panel-header .panel-title-up, 133 | .window.panel-hright>.panel-header .panel-title-up { 134 | bottom: 0; 135 | } 136 | .window.panel-hleft .window-body { 137 | border-width: 1px 1px 1px 0; 138 | } 139 | .window.panel-hright .window-body { 140 | border-width: 1px 0 1px 1px; 141 | } 142 | .window.panel-hleft .window-header .panel-icon { 143 | top: 1px; 144 | margin-top: 0; 145 | left: 0; 146 | } 147 | .window.panel-hright .window-header .panel-icon { 148 | top: 1px; 149 | margin-top: 0; 150 | left: auto; 151 | right: 1px; 152 | } 153 | .window.panel-hleft .window-header .panel-tool, 154 | .window.panel-hright .window-header .panel-tool { 155 | margin-top: 0; 156 | top: auto; 157 | bottom: 1px; 158 | right: auto; 159 | margin-right: 0; 160 | left: 50%; 161 | margin-left: -11px; 162 | } 163 | .window.panel-hright .window-header .panel-tool { 164 | left: auto; 165 | right: 1px; 166 | } 167 | .window-thinborder.panel-hleft .window-header { 168 | padding: 5px 6px 5px 5px; 169 | } 170 | .window-thinborder.panel-hright .window-header { 171 | padding: 5px 5px 5px 6px; 172 | } 173 | .window-thinborder.panel-hleft>.panel-header .panel-title { 174 | left: 21px; 175 | } 176 | .window-thinborder.panel-hleft>.panel-header .panel-title-up, 177 | .window-thinborder.panel-hright>.panel-header .panel-title-up { 178 | bottom: 5px; 179 | } 180 | .window-thinborder.panel-hleft .window-header .panel-icon, 181 | .window-thinborder.panel-hright .window-header .panel-icon { 182 | margin-top: 5px; 183 | } 184 | .window-thinborder.panel-hleft .window-header .panel-tool, 185 | .window-thinborder.panel-hright .window-header .panel-tool { 186 | left: 16px; 187 | bottom: 5px; 188 | } 189 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/linkbutton.css: -------------------------------------------------------------------------------- 1 | .l-btn { 2 | text-decoration: none; 3 | display: inline-block; 4 | overflow: hidden; 5 | margin: 0; 6 | padding: 0; 7 | cursor: pointer; 8 | outline: none; 9 | text-align: center; 10 | vertical-align: middle; 11 | line-height: normal; 12 | } 13 | .l-btn-plain { 14 | border-width: 0; 15 | padding: 1px; 16 | } 17 | .l-btn-left { 18 | display: inline-block; 19 | position: relative; 20 | overflow: hidden; 21 | margin: 0; 22 | padding: 0; 23 | vertical-align: top; 24 | } 25 | .l-btn-text { 26 | display: inline-block; 27 | vertical-align: top; 28 | width: auto; 29 | line-height: 28px; 30 | font-size: 14px; 31 | padding: 0; 32 | margin: 0 6px; 33 | } 34 | .l-btn-icon { 35 | display: inline-block; 36 | width: 16px; 37 | height: 16px; 38 | line-height: 16px; 39 | position: absolute; 40 | top: 50%; 41 | margin-top: -8px; 42 | font-size: 1px; 43 | } 44 | .l-btn span span .l-btn-empty { 45 | display: inline-block; 46 | margin: 0; 47 | width: 16px; 48 | height: 24px; 49 | font-size: 1px; 50 | vertical-align: top; 51 | } 52 | .l-btn span .l-btn-icon-left { 53 | padding: 0 0 0 20px; 54 | background-position: left center; 55 | } 56 | .l-btn span .l-btn-icon-right { 57 | padding: 0 20px 0 0; 58 | background-position: right center; 59 | } 60 | .l-btn-icon-left .l-btn-text { 61 | margin: 0 6px 0 26px; 62 | } 63 | .l-btn-icon-left .l-btn-icon { 64 | left: 6px; 65 | } 66 | .l-btn-icon-right .l-btn-text { 67 | margin: 0 26px 0 6px; 68 | } 69 | .l-btn-icon-right .l-btn-icon { 70 | right: 6px; 71 | } 72 | .l-btn-icon-top .l-btn-text { 73 | margin: 20px 4px 0 4px; 74 | } 75 | .l-btn-icon-top .l-btn-icon { 76 | top: 4px; 77 | left: 50%; 78 | margin: 0 0 0 -8px; 79 | } 80 | .l-btn-icon-bottom .l-btn-text { 81 | margin: 0 4px 20px 4px; 82 | } 83 | .l-btn-icon-bottom .l-btn-icon { 84 | top: auto; 85 | bottom: 4px; 86 | left: 50%; 87 | margin: 0 0 0 -8px; 88 | } 89 | .l-btn-left .l-btn-empty { 90 | margin: 0 6px; 91 | width: 16px; 92 | } 93 | .l-btn-plain:hover { 94 | padding: 0; 95 | } 96 | .l-btn-focus { 97 | outline: #0000FF dotted thin; 98 | } 99 | .l-btn-large .l-btn-text { 100 | line-height: 44px; 101 | } 102 | .l-btn-large .l-btn-icon { 103 | width: 32px; 104 | height: 32px; 105 | line-height: 32px; 106 | margin-top: -16px; 107 | } 108 | .l-btn-large .l-btn-icon-left .l-btn-text { 109 | margin-left: 40px; 110 | } 111 | .l-btn-large .l-btn-icon-right .l-btn-text { 112 | margin-right: 40px; 113 | } 114 | .l-btn-large .l-btn-icon-top .l-btn-text { 115 | margin-top: 36px; 116 | line-height: 24px; 117 | min-width: 32px; 118 | } 119 | .l-btn-large .l-btn-icon-top .l-btn-icon { 120 | margin: 0 0 0 -16px; 121 | } 122 | .l-btn-large .l-btn-icon-bottom .l-btn-text { 123 | margin-bottom: 36px; 124 | line-height: 24px; 125 | min-width: 32px; 126 | } 127 | .l-btn-large .l-btn-icon-bottom .l-btn-icon { 128 | margin: 0 0 0 -16px; 129 | } 130 | .l-btn-large .l-btn-left .l-btn-empty { 131 | margin: 0 6px; 132 | width: 32px; 133 | } 134 | .l-btn { 135 | color: #444; 136 | background: #fafafa; 137 | background-repeat: repeat-x; 138 | border: 1px solid #bbb; 139 | background: -webkit-linear-gradient(top,#ffffff 0,#eeeeee 100%); 140 | background: -moz-linear-gradient(top,#ffffff 0,#eeeeee 100%); 141 | background: -o-linear-gradient(top,#ffffff 0,#eeeeee 100%); 142 | background: linear-gradient(to bottom,#ffffff 0,#eeeeee 100%); 143 | background-repeat: repeat-x; 144 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff,endColorstr=#eeeeee,GradientType=0); 145 | -moz-border-radius: 5px 5px 5px 5px; 146 | -webkit-border-radius: 5px 5px 5px 5px; 147 | border-radius: 5px 5px 5px 5px; 148 | } 149 | .l-btn:hover { 150 | background: #eaf2ff; 151 | color: #000000; 152 | border: 1px solid #b7d2ff; 153 | filter: none; 154 | } 155 | .l-btn-plain { 156 | background: transparent; 157 | border-width: 0; 158 | filter: none; 159 | } 160 | .l-btn-outline { 161 | border-width: 1px; 162 | border-color: #b7d2ff; 163 | padding: 0; 164 | } 165 | .l-btn-plain:hover { 166 | background: #eaf2ff; 167 | color: #000000; 168 | border: 1px solid #b7d2ff; 169 | -moz-border-radius: 5px 5px 5px 5px; 170 | -webkit-border-radius: 5px 5px 5px 5px; 171 | border-radius: 5px 5px 5px 5px; 172 | } 173 | .l-btn-disabled, 174 | .l-btn-disabled:hover { 175 | opacity: 0.5; 176 | cursor: default; 177 | background: #fafafa; 178 | color: #444; 179 | background: -webkit-linear-gradient(top,#ffffff 0,#eeeeee 100%); 180 | background: -moz-linear-gradient(top,#ffffff 0,#eeeeee 100%); 181 | background: -o-linear-gradient(top,#ffffff 0,#eeeeee 100%); 182 | background: linear-gradient(to bottom,#ffffff 0,#eeeeee 100%); 183 | background-repeat: repeat-x; 184 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff,endColorstr=#eeeeee,GradientType=0); 185 | } 186 | .l-btn-disabled .l-btn-text, 187 | .l-btn-disabled .l-btn-icon { 188 | filter: alpha(opacity=50); 189 | } 190 | .l-btn-plain-disabled, 191 | .l-btn-plain-disabled:hover { 192 | background: transparent; 193 | filter: alpha(opacity=50); 194 | } 195 | .l-btn-selected, 196 | .l-btn-selected:hover { 197 | background: #ddd; 198 | filter: none; 199 | } 200 | .l-btn-plain-selected, 201 | .l-btn-plain-selected:hover { 202 | background: #ddd; 203 | } 204 | -------------------------------------------------------------------------------- /lib/ConfigUtil.js: -------------------------------------------------------------------------------- 1 | class ConfigUtil { 2 | constructor(config) { 3 | this.config = config; 4 | } 5 | 6 | isConfigGateway(gatewaySid) { 7 | if(this.config['gateways'] && this.config['gateways'][gatewaySid]) { 8 | return true; 9 | } 10 | 11 | return false; 12 | } 13 | 14 | isHostGateway(gatewaySid) { 15 | if(this.config['gateways'] && this.config['gateways'][gatewaySid]) { 16 | if(this.config['gateways'][gatewaySid] instanceof Object) { 17 | if(this.config['gateways'][gatewaySid]['ip']) { 18 | return true; 19 | } 20 | } 21 | } 22 | 23 | return false; 24 | } 25 | 26 | getHosts() { 27 | var hosts = {}; 28 | 29 | var gateways = this.getGateways(); 30 | if(gateways) { 31 | for(var gatewaySid in gateways) { 32 | if(gateways[gatewaySid] instanceof Object) { 33 | if(gateways[gatewaySid]['ip']) { 34 | hosts[gatewaySid] = new Object(); 35 | hosts[gatewaySid].ip = gateways[gatewaySid]['ip']; 36 | if(!gateways[gatewaySid]['port']) { 37 | hosts[gatewaySid].port = '9898'; 38 | } else { 39 | hosts[gatewaySid].port = gateways[gatewaySid]['port']; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | return hosts; 47 | } 48 | 49 | getGateways() { 50 | return this.config['gateways']; 51 | } 52 | 53 | getBindAddress() { 54 | return this.config['bindAddress']; 55 | } 56 | 57 | getSendWhoisCmdInterval() { 58 | return this.config['sendWhoisCmdInterval'] || 1 * 60 * 60 * 1000; 59 | } 60 | 61 | getAutoRemoveAccessoryInterval() { 62 | return this.config['autoRemoveAccessoryInterval']; 63 | } 64 | 65 | getMQTTConfig() { 66 | if(this.config['mqtt'] instanceof Object) { 67 | return this.config['mqtt']; 68 | } 69 | return null; 70 | } 71 | 72 | getManagePort() { 73 | if(this.config['manage'] instanceof Object) { 74 | return this.config['manage']['port']; 75 | } 76 | return null; 77 | } 78 | 79 | getManagePassword() { 80 | if(this.config['manage'] instanceof Object) { 81 | return this.config['manage']['password']; 82 | } 83 | return null; 84 | } 85 | 86 | getGatewayPasswordByGatewaySid(gatewaySid) { 87 | if(this.config['gateways'][gatewaySid] instanceof Object) { 88 | return this.config['gateways'][gatewaySid]['password']; 89 | } else { 90 | return this.config['gateways'][gatewaySid]; 91 | } 92 | } 93 | 94 | getAccessoryConfig(deviceSid) { 95 | var result = {}; 96 | if(this.config['defaultValue']) { 97 | result = this.config['defaultValue'][deviceSid]; 98 | } 99 | return result; 100 | } 101 | 102 | getAccessoryAttribute(deviceSid, accessoryType, attributeName, defaultValue) { 103 | var defaultValueCfg = this.config['defaultValue']; 104 | if(null != defaultValueCfg) { 105 | if(null != defaultValueCfg[deviceSid]) { 106 | if(null != defaultValueCfg[deviceSid][accessoryType]) { 107 | if(null != defaultValueCfg[deviceSid][accessoryType][attributeName]) { 108 | return defaultValueCfg[deviceSid][accessoryType][attributeName]; 109 | } 110 | } 111 | if(null != defaultValueCfg[deviceSid]['Global']){ 112 | if(null != defaultValueCfg[deviceSid]['Global'][attributeName]) { 113 | return defaultValueCfg[deviceSid]['Global'][attributeName]; 114 | } 115 | } 116 | } 117 | if(null != defaultValueCfg['Global']) { 118 | if(null != defaultValueCfg['Global'][attributeName]) { 119 | return defaultValueCfg['Global'][attributeName]; 120 | } 121 | } 122 | 123 | } 124 | 125 | return defaultValue; 126 | } 127 | 128 | getAccessoryName(deviceSid, accessoryType) { 129 | var defaultValue = accessoryType + "_" + deviceSid.substring(deviceSid.length - 4); 130 | return this.getAccessoryAttribute(deviceSid, accessoryType, 'name', defaultValue); 131 | } 132 | 133 | getAccessoryDisable(deviceSid, accessoryType) { 134 | return this.getAccessoryAttribute(deviceSid, accessoryType, 'disable', false); 135 | } 136 | 137 | getAccessoryServiceType(deviceSid, accessoryType) { 138 | return this.getAccessoryAttribute(deviceSid, accessoryType, 'serviceType', null); 139 | } 140 | 141 | getAccessorySyncValue(deviceSid, accessoryType) { 142 | return this.getAccessoryAttribute(deviceSid, accessoryType, 'syncValue', false); 143 | } 144 | 145 | getAccessoryNoResponse(deviceSid, accessoryType) { 146 | return this.getAccessoryAttribute(deviceSid, accessoryType, 'disableNoResponse', false); 147 | } 148 | 149 | getAccessoryIgnoreWriteResult(deviceSid, accessoryType) { 150 | return this.getAccessoryAttribute(deviceSid, accessoryType, 'ignoreWriteResult', false); 151 | } 152 | } 153 | 154 | module.exports = ConfigUtil; -------------------------------------------------------------------------------- /parser/PlugBaseParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class PlugBaseParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'PlugBase_Outlet': PlugBaseOutletParser 12 | } 13 | } 14 | } 15 | PlugBaseParser.modelName = ['plug']; 16 | module.exports = PlugBaseParser; 17 | 18 | class PlugBaseOutletParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.OUTLET; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Plug Base', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.Outlet(accessoryName); 40 | service.getCharacteristic(that.Characteristic.On); 41 | service.getCharacteristic(that.Characteristic.OutletInUse); 42 | result.push(service); 43 | 44 | return result; 45 | } 46 | 47 | parserAccessories(jsonObj) { 48 | var that = this; 49 | var deviceSid = jsonObj['sid']; 50 | var uuid = that.getAccessoryUUID(deviceSid); 51 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 52 | if(accessory) { 53 | var service = accessory.getService(that.Service.Outlet); 54 | var onCharacteristic = service.getCharacteristic(that.Characteristic.On); 55 | var outletInUseCharacteristic = service.getCharacteristic(that.Characteristic.OutletInUse); 56 | var value = that.getOnCharacteristicValue(jsonObj, null); 57 | if(null != value) { 58 | onCharacteristic.updateValue(value); 59 | outletInUseCharacteristic.updateValue(value); 60 | } 61 | 62 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 63 | if (onCharacteristic.listeners('get').length == 0) { 64 | onCharacteristic.on("get", function(callback) { 65 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 66 | that.platform.sendReadCommand(deviceSid, command).then(result => { 67 | var value = that.getOnCharacteristicValue(result, null); 68 | if(null != value) { 69 | outletInUseCharacteristic.updateValue(value); 70 | callback(null, value); 71 | } else { 72 | callback(new Error('get value fail: ' + result)); 73 | } 74 | }).catch(function(err) { 75 | that.platform.log.error(err); 76 | callback(err); 77 | }); 78 | }); 79 | } 80 | } 81 | 82 | if (onCharacteristic.listeners('set').length == 0) { 83 | onCharacteristic.on("set", function(value, callback) { 84 | var model = that.platform.getDeviceModelBySid(deviceSid); 85 | var command = null; 86 | var proto_version_prefix = that.platform.getProtoVersionPrefixByProtoVersion(that.platform.getDeviceProtoVersionBySid(deviceSid)); 87 | if(1 == proto_version_prefix) { 88 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"status":"' + (value ? 'on' : 'off') + '", "key": "${key}"}}'; 89 | } else if(2 == proto_version_prefix) { 90 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_0":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 91 | } else { 92 | } 93 | 94 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 95 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 96 | that.callback2HB(deviceSid, this, callback, null); 97 | } else { 98 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 99 | that.callback2HB(deviceSid, this, callback, null); 100 | }).catch(function(err) { 101 | that.platform.log.error(err); 102 | that.callback2HB(deviceSid, this, callback, err); 103 | }); 104 | } 105 | }); 106 | } 107 | } 108 | } 109 | 110 | getOnCharacteristicValue(jsonObj, defaultValue) { 111 | var value = null; 112 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 113 | if(1 == proto_version_prefix) { 114 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 115 | } else if(2 == proto_version_prefix) { 116 | value = this.getValueFrJsonObjData2(jsonObj, 'channel_0'); 117 | } else { 118 | } 119 | 120 | if(value === 'on') { 121 | return true; 122 | } else if(value === 'off') { 123 | return false; 124 | } else { 125 | return defaultValue; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /parser/PlugBase86Parser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class PlugBase86Parser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'PlugBase86_Outlet': PlugBase86OutletParser 12 | } 13 | } 14 | } 15 | PlugBase86Parser.modelName = ['86plug', 'ctrl_86plug', 'ctrl_86plug.aq1']; 16 | module.exports = PlugBase86Parser; 17 | 18 | class PlugBase86OutletParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.OUTLET; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Plug Base 86', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.Outlet(accessoryName); 40 | service.getCharacteristic(that.Characteristic.On); 41 | service.getCharacteristic(that.Characteristic.OutletInUse); 42 | result.push(service); 43 | 44 | return result; 45 | } 46 | 47 | parserAccessories(jsonObj) { 48 | var that = this; 49 | var deviceSid = jsonObj['sid']; 50 | var uuid = that.getAccessoryUUID(deviceSid); 51 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 52 | if(accessory) { 53 | var service = accessory.getService(that.Service.Outlet); 54 | var onCharacteristic = service.getCharacteristic(that.Characteristic.On); 55 | var outletInUseCharacteristic = service.getCharacteristic(that.Characteristic.OutletInUse); 56 | var value = that.getOnCharacteristicValue(jsonObj, null); 57 | if(null != value) { 58 | onCharacteristic.updateValue(value); 59 | outletInUseCharacteristic.updateValue(value); 60 | } 61 | 62 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 63 | if (onCharacteristic.listeners('get').length == 0) { 64 | onCharacteristic.on("get", function(callback) { 65 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 66 | that.platform.sendReadCommand(deviceSid, command).then(result => { 67 | var value = that.getOnCharacteristicValue(result, null); 68 | if(null != value) { 69 | outletInUseCharacteristic.updateValue(value); 70 | callback(null, value); 71 | } else { 72 | callback(new Error('get value fail: ' + result)); 73 | } 74 | }).catch(function(err) { 75 | that.platform.log.error(err); 76 | callback(err); 77 | }); 78 | }); 79 | } 80 | } 81 | 82 | if (onCharacteristic.listeners('set').length == 0) { 83 | onCharacteristic.on("set", function(value, callback) { 84 | var model = that.platform.getDeviceModelBySid(deviceSid); 85 | var command = null; 86 | var proto_version_prefix = that.platform.getProtoVersionPrefixByProtoVersion(that.platform.getDeviceProtoVersionBySid(deviceSid)); 87 | if(1 == proto_version_prefix) { 88 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"status":"' + (value ? 'on' : 'off') + '", "key": "${key}"}}'; 89 | } else if(2 == proto_version_prefix) { 90 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_0":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 91 | } else { 92 | } 93 | 94 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 95 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 96 | that.callback2HB(deviceSid, this, callback, null); 97 | } else { 98 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 99 | that.callback2HB(deviceSid, this, callback, null); 100 | }).catch(function(err) { 101 | that.platform.log.error(err); 102 | that.callback2HB(deviceSid, this, callback, err); 103 | }); 104 | } 105 | }); 106 | } 107 | } 108 | } 109 | 110 | getOnCharacteristicValue(jsonObj, defaultValue) { 111 | var value = null; 112 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 113 | if(1 == proto_version_prefix) { 114 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 115 | } else if(2 == proto_version_prefix) { 116 | value = this.getValueFrJsonObjData2(jsonObj, 'channel_0'); 117 | } else { 118 | } 119 | 120 | if(value === 'on') { 121 | return true; 122 | } else if(value === 'off') { 123 | return false; 124 | } else { 125 | return defaultValue; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /parser/ElectricCurtainParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class ElectricCurtainParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'ElectricCurtain_WindowCovering': ElectricCurtainWindowCoveringParser 12 | } 13 | } 14 | } 15 | ElectricCurtainParser.modelName = ['curtain']; 16 | module.exports = ElectricCurtainParser; 17 | 18 | class ElectricCurtainWindowCoveringParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Electric Curtain', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.WindowCovering(accessoryName); 40 | service.getCharacteristic(that.Characteristic.PositionState); 41 | service.getCharacteristic(that.Characteristic.CurrentPosition); 42 | service.getCharacteristic(that.Characteristic.TargetPosition); 43 | result.push(service); 44 | 45 | return result; 46 | } 47 | 48 | parserAccessories(jsonObj) { 49 | var that = this; 50 | var deviceSid = jsonObj['sid']; 51 | var uuid = that.getAccessoryUUID(deviceSid); 52 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 53 | if(accessory) { 54 | var service = accessory.getService(that.Service.WindowCovering); 55 | var positionStateCharacteristic = service.getCharacteristic(that.Characteristic.PositionState); 56 | var currentPositionCharacteristic = service.getCharacteristic(that.Characteristic.CurrentPosition); 57 | var targetPositionCharacteristic = service.getCharacteristic(that.Characteristic.TargetPosition); 58 | var value = that.getCurrentPositionCharacteristicValue(jsonObj, null); 59 | if(null != value) { 60 | positionStateCharacteristic.updateValue(that.Characteristic.PositionState.STOPPED); 61 | currentPositionCharacteristic.updateValue(value); 62 | targetPositionCharacteristic.updateValue(value); 63 | } 64 | 65 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 66 | if (currentPositionCharacteristic.listeners('get').length == 0) { 67 | currentPositionCharacteristic.on("get", function(callback) { 68 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 69 | that.platform.sendReadCommand(deviceSid, command).then(result => { 70 | var value = that.getCurrentPositionCharacteristicValue(result, null); 71 | if(null != value) { 72 | positionStateCharacteristic.updateValue(that.Characteristic.PositionState.STOPPED); 73 | targetPositionCharacteristic.updateValue(value); 74 | callback(null, value); 75 | } else { 76 | callback(new Error('get value fail: ' + result)); 77 | } 78 | }).catch(function(err) { 79 | that.platform.log.error(err); 80 | callback(err); 81 | }); 82 | }); 83 | } 84 | } 85 | 86 | if (targetPositionCharacteristic.listeners('set').length == 0) { 87 | targetPositionCharacteristic.on("set", function(value, callback) { 88 | var model = that.platform.getDeviceModelBySid(deviceSid); 89 | var command = null; 90 | var proto_version_prefix = that.platform.getProtoVersionPrefixByProtoVersion(that.platform.getDeviceProtoVersionBySid(deviceSid)); 91 | if(1 == proto_version_prefix) { 92 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"curtain_level":"' + value + '", "key": "${key}"}}'; 93 | } else if(2 == proto_version_prefix) { 94 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"curtain_level":' + value + '}], "key": "${key}"}'; 95 | } else { 96 | } 97 | 98 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 99 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 100 | that.callback2HB(deviceSid, this, callback, null); 101 | } else { 102 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 103 | that.callback2HB(deviceSid, this, callback, null); 104 | }).catch(function(err) { 105 | that.platform.log.error(err); 106 | that.callback2HB(deviceSid, this, callback, err); 107 | }); 108 | } 109 | }); 110 | } 111 | } 112 | } 113 | 114 | getCurrentPositionCharacteristicValue(jsonObj, defaultValue) { 115 | var value = this.getValueFrJsonObjData(jsonObj, 'curtain_level'); 116 | if(value / 1.0 > 100) { 117 | return defaultValue; 118 | } else { 119 | return value / 1.0; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /parser/SingleSwitchParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class SingleSwitchParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'SingleSwitch_Switch': SingleSwitchSwitchParser 12 | } 13 | } 14 | } 15 | SingleSwitchParser.modelName = ['ctrl_neutral1']; 16 | module.exports = SingleSwitchParser; 17 | 18 | class SingleSwitchSwitchParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | var serviceType = this.platform.ConfigUtil.getAccessoryServiceType(deviceSid, this.accessoryType); 25 | if(serviceType == 'Lightbulb') { 26 | return this.Accessory.Categories.LIGHTBULB; 27 | } else { 28 | return this.Accessory.Categories.SWITCH; 29 | } 30 | } 31 | 32 | getAccessoryInformation(deviceSid) { 33 | return { 34 | 'Manufacturer': 'Aqara', 35 | 'Model': 'Single Switch', 36 | 'SerialNumber': deviceSid 37 | }; 38 | } 39 | 40 | getServices(jsonObj, accessoryName) { 41 | var that = this; 42 | var result = []; 43 | 44 | var service = null; 45 | var deviceSid = jsonObj['sid']; 46 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 47 | if(serviceType == 'Lightbulb') { 48 | service = new that.Service.Lightbulb(accessoryName); 49 | } else { 50 | service = new that.Service.Switch(accessoryName); 51 | } 52 | service.getCharacteristic(that.Characteristic.On); 53 | result.push(service); 54 | 55 | return result; 56 | } 57 | 58 | parserAccessories(jsonObj) { 59 | var that = this; 60 | var deviceSid = jsonObj['sid']; 61 | var uuid = that.getAccessoryUUID(deviceSid); 62 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 63 | if(accessory) { 64 | var service = null; 65 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 66 | if(serviceType == 'Lightbulb') { 67 | service = accessory.getService(that.Service.Lightbulb); 68 | } else { 69 | service = accessory.getService(that.Service.Switch); 70 | } 71 | var onCharacteristic = service.getCharacteristic(that.Characteristic.On); 72 | var value = that.getOnCharacteristicValue(jsonObj, null); 73 | if(null != value) { 74 | onCharacteristic.updateValue(value); 75 | } 76 | 77 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 78 | if (onCharacteristic.listeners('get').length == 0) { 79 | onCharacteristic.on("get", function(callback) { 80 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 81 | that.platform.sendReadCommand(deviceSid, command).then(result => { 82 | var value = that.getOnCharacteristicValue(result, null); 83 | if(null != value) { 84 | callback(null, value); 85 | } else { 86 | callback(new Error('get value fail: ' + result)); 87 | } 88 | }).catch(function(err) { 89 | that.platform.log.error(err); 90 | callback(err); 91 | }); 92 | }); 93 | } 94 | } 95 | 96 | if(onCharacteristic.listeners('set').length == 0) { 97 | onCharacteristic.on("set", function(value, callback) { 98 | var model = that.platform.getDeviceModelBySid(deviceSid); 99 | var command = null; 100 | var proto_version_prefix = that.platform.getProtoVersionPrefixByProtoVersion(that.platform.getDeviceProtoVersionBySid(deviceSid)); 101 | if(1 == proto_version_prefix) { 102 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"channel_0":"' + (value ? 'on' : 'off') + '", "key": "${key}"}"}'; 103 | } else if(2 == proto_version_prefix) { 104 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_0":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 105 | } else { 106 | } 107 | 108 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 109 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 110 | that.callback2HB(deviceSid, this, callback, null); 111 | } else { 112 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 113 | that.callback2HB(deviceSid, this, callback, null); 114 | }).catch(function(err) { 115 | that.platform.log.error(err); 116 | that.callback2HB(deviceSid, this, callback, err); 117 | }); 118 | } 119 | }); 120 | } 121 | } 122 | } 123 | 124 | getOnCharacteristicValue(jsonObj, defaultValue) { 125 | var value = this.getValueFrJsonObjData(jsonObj, 'channel_0'); 126 | if(value === 'on') { 127 | return true; 128 | } else if(value === 'off') { 129 | return false; 130 | } else { 131 | return defaultValue; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /parser/SingleSwitchLNParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class SingleSwitchLNParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'SingleSwitchLN_Switch': SingleSwitchLNSwitchParser 12 | } 13 | } 14 | } 15 | SingleSwitchLNParser.modelName = ['ctrl_ln1', 'ctrl_ln1.aq1']; 16 | module.exports = SingleSwitchLNParser; 17 | 18 | class SingleSwitchLNSwitchParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | var serviceType = this.platform.ConfigUtil.getAccessoryServiceType(deviceSid, this.accessoryType); 25 | if(serviceType == 'Lightbulb') { 26 | return this.Accessory.Categories.LIGHTBULB; 27 | } else { 28 | return this.Accessory.Categories.SWITCH; 29 | } 30 | } 31 | 32 | getAccessoryInformation(deviceSid) { 33 | return { 34 | 'Manufacturer': 'Aqara', 35 | 'Model': 'Single Switch LN', 36 | 'SerialNumber': deviceSid 37 | }; 38 | } 39 | 40 | getServices(jsonObj, accessoryName) { 41 | var that = this; 42 | var result = []; 43 | 44 | var service = null; 45 | var deviceSid = jsonObj['sid']; 46 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 47 | if(serviceType == 'Lightbulb') { 48 | service = new that.Service.Lightbulb(accessoryName); 49 | } else { 50 | service = new that.Service.Switch(accessoryName); 51 | } 52 | service.getCharacteristic(that.Characteristic.On); 53 | result.push(service); 54 | 55 | return result; 56 | } 57 | 58 | parserAccessories(jsonObj) { 59 | var that = this; 60 | var deviceSid = jsonObj['sid']; 61 | var uuid = that.getAccessoryUUID(deviceSid); 62 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 63 | if(accessory) { 64 | var service = null; 65 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 66 | if(serviceType == 'Lightbulb') { 67 | service = accessory.getService(that.Service.Lightbulb); 68 | } else { 69 | service = accessory.getService(that.Service.Switch); 70 | } 71 | var onCharacteristic = service.getCharacteristic(that.Characteristic.On); 72 | var value = that.getOnCharacteristicValue(jsonObj, null); 73 | if(null != value) { 74 | onCharacteristic.updateValue(value); 75 | } 76 | 77 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 78 | if (onCharacteristic.listeners('get').length == 0) { 79 | onCharacteristic.on("get", function(callback) { 80 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 81 | that.platform.sendReadCommand(deviceSid, command).then(result => { 82 | var value = that.getOnCharacteristicValue(result, null); 83 | if(null != value) { 84 | callback(null, value); 85 | } else { 86 | callback(new Error('get value fail: ' + result)); 87 | } 88 | }).catch(function(err) { 89 | that.platform.log.error(err); 90 | callback(err); 91 | }); 92 | }); 93 | } 94 | } 95 | 96 | if(onCharacteristic.listeners('set').length == 0) { 97 | onCharacteristic.on("set", function(value, callback) { 98 | var model = that.platform.getDeviceModelBySid(deviceSid); 99 | var command = null; 100 | var proto_version_prefix = that.platform.getProtoVersionPrefixByProtoVersion(that.platform.getDeviceProtoVersionBySid(deviceSid)); 101 | if(1 == proto_version_prefix) { 102 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"channel_0":"' + (value ? 'on' : 'off') + '", "key": "${key}"}}'; 103 | } else if(2 == proto_version_prefix) { 104 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_0":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 105 | } else { 106 | } 107 | 108 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 109 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 110 | that.callback2HB(deviceSid, this, callback, null); 111 | } else { 112 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 113 | that.callback2HB(deviceSid, this, callback, null); 114 | }).catch(function(err) { 115 | that.platform.log.error(err); 116 | that.callback2HB(deviceSid, this, callback, err); 117 | }); 118 | } 119 | }); 120 | } 121 | } 122 | } 123 | 124 | getOnCharacteristicValue(jsonObj, defaultValue) { 125 | var value = this.getValueFrJsonObjData(jsonObj, 'channel_0'); 126 | if(value === 'on') { 127 | return true; 128 | } else if(value === 'off') { 129 | return false; 130 | } else { 131 | return defaultValue; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /parser/VibrationSensorParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class VibrationSensor extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'VibrationSensor_MotionSensor_Vibrate': VibrationSensorMotionSensorVibrateParser, 12 | 'VibrationSensor_MotionSensor_Tilt': VibrationSensorMotionSensorTiltParser, 13 | 'VibrationSensor_MotionSensor_FreeFall': VibrationSensorMotionSensorFreeFallParser 14 | } 15 | } 16 | } 17 | VibrationSensor.modelName = ['vibration']; 18 | module.exports = VibrationSensor; 19 | 20 | class VibrationSensorMotionSensorBaseParser extends AccessoryParser { 21 | constructor(platform, accessoryType) { 22 | super(platform, accessoryType) 23 | } 24 | 25 | getAccessoryCategory(deviceSid) { 26 | return this.Accessory.Categories.SENSOR; 27 | } 28 | 29 | getAccessoryInformation(deviceSid) { 30 | return { 31 | 'Manufacturer': 'Aqara', 32 | 'Model': 'Vibration Sensor', 33 | 'SerialNumber': deviceSid 34 | }; 35 | } 36 | 37 | getServices(jsonObj, accessoryName) { 38 | var that = this; 39 | var result = []; 40 | 41 | var service = new that.Service.MotionSensor(accessoryName); 42 | service.getCharacteristic(that.Characteristic.MotionDetected); 43 | result.push(service); 44 | 45 | var batteryService = new that.Service.BatteryService(accessoryName); 46 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 47 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 48 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 49 | result.push(batteryService); 50 | 51 | return result; 52 | } 53 | 54 | parserAccessories(jsonObj) { 55 | var that = this; 56 | var deviceSid = jsonObj['sid']; 57 | var uuid = that.getAccessoryUUID(deviceSid); 58 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 59 | if(accessory) { 60 | var service = accessory.getService(that.Service.MotionSensor); 61 | var motionDetectedCharacteristic = service.getCharacteristic(that.Characteristic.MotionDetected); 62 | var value = that.getMotionDetectedCharacteristicValue(jsonObj, null); 63 | if(null != value) { 64 | motionDetectedCharacteristic.updateValue(value); 65 | if(true == value) { 66 | setTimeout(() => { 67 | motionDetectedCharacteristic.updateValue(false); 68 | }, 12 * 1000); 69 | } 70 | } 71 | /* 72 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 73 | if (motionDetectedCharacteristic.listeners('get').length == 0) { 74 | motionDetectedCharacteristic.on("get", function(callback) { 75 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 76 | that.platform.sendReadCommand(deviceSid, command).then(result => { 77 | var value = that.getMotionDetectedCharacteristicValue(result, null); 78 | if(null != value) { 79 | callback(null, value); 80 | } else { 81 | callback(new Error('get value fail: ' + result)); 82 | } 83 | }).catch(function(err) { 84 | that.platform.log.error(err); 85 | callback(err); 86 | }); 87 | }); 88 | } 89 | } 90 | */ 91 | that.parserBatteryService(accessory, jsonObj); 92 | } 93 | } 94 | } 95 | 96 | class VibrationSensorMotionSensorVibrateParser extends VibrationSensorMotionSensorBaseParser { 97 | getMotionDetectedCharacteristicValue(jsonObj, defaultValue) { 98 | var value = null; 99 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 100 | if(1 == proto_version_prefix) { 101 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 102 | } else if(2 == proto_version_prefix) { 103 | value = this.getValueFrJsonObjData2(jsonObj, 'motion_status'); 104 | } else { 105 | } 106 | 107 | return (value === 'vibrate') ? true : null; 108 | } 109 | } 110 | 111 | class VibrationSensorMotionSensorTiltParser extends VibrationSensorMotionSensorBaseParser { 112 | getMotionDetectedCharacteristicValue(jsonObj, defaultValue) { 113 | var value = null; 114 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 115 | if(1 == proto_version_prefix) { 116 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 117 | } else if(2 == proto_version_prefix) { 118 | value = this.getValueFrJsonObjData2(jsonObj, 'motion_status'); 119 | } else { 120 | } 121 | 122 | return (value === 'tilt') ? true : null; 123 | } 124 | } 125 | 126 | class VibrationSensorMotionSensorFreeFallParser extends VibrationSensorMotionSensorBaseParser { 127 | getMotionDetectedCharacteristicValue(jsonObj, defaultValue) { 128 | var value = null; 129 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 130 | if(1 == proto_version_prefix) { 131 | value = this.getValueFrJsonObjData1(jsonObj, 'status'); 132 | } else if(2 == proto_version_prefix) { 133 | value = this.getValueFrJsonObjData2(jsonObj, 'motion_status'); 134 | } else { 135 | } 136 | 137 | return (value === 'free_fall') ? true : null; 138 | } 139 | } -------------------------------------------------------------------------------- /parser/ElectricCurtainBatteryParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class ElectricCurtainBatteryParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'ElectricCurtainBattery_WindowCovering': ElectricCurtainBatteryWindowCoveringParser 12 | } 13 | } 14 | } 15 | ElectricCurtainBatteryParser.modelName = ['curtain.hagl04']; 16 | module.exports = ElectricCurtainBatteryParser; 17 | 18 | class ElectricCurtainBatteryWindowCoveringParser extends AccessoryParser { 19 | constructor(platform, accessoryType) { 20 | super(platform, accessoryType) 21 | } 22 | 23 | getAccessoryCategory(deviceSid) { 24 | return this.Accessory.Categories.SENSOR; 25 | } 26 | 27 | getAccessoryInformation(deviceSid) { 28 | return { 29 | 'Manufacturer': 'Aqara', 30 | 'Model': 'Electric Curtain B1', 31 | 'SerialNumber': deviceSid 32 | }; 33 | } 34 | 35 | getServices(jsonObj, accessoryName) { 36 | var that = this; 37 | var result = []; 38 | 39 | var service = new that.Service.WindowCovering(accessoryName); 40 | service.getCharacteristic(that.Characteristic.PositionState); 41 | service.getCharacteristic(that.Characteristic.CurrentPosition); 42 | service.getCharacteristic(that.Characteristic.TargetPosition); 43 | result.push(service); 44 | 45 | var batteryService = new that.Service.BatteryService(accessoryName); 46 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 47 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 48 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 49 | result.push(batteryService); 50 | 51 | return result; 52 | } 53 | 54 | parserAccessories(jsonObj) { 55 | var that = this; 56 | var deviceSid = jsonObj['sid']; 57 | var uuid = that.getAccessoryUUID(deviceSid); 58 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 59 | if (accessory) { 60 | var service = accessory.getService(that.Service.WindowCovering); 61 | var positionStateCharacteristic = service.getCharacteristic(that.Characteristic.PositionState); 62 | var currentPositionCharacteristic = service.getCharacteristic(that.Characteristic.CurrentPosition); 63 | var targetPositionCharacteristic = service.getCharacteristic(that.Characteristic.TargetPosition); 64 | var value = that.getCurrentPositionCharacteristicValue(jsonObj, null); 65 | if (null != value) { 66 | positionStateCharacteristic.updateValue(that.Characteristic.PositionState.STOPPED); 67 | currentPositionCharacteristic.updateValue(value); 68 | targetPositionCharacteristic.updateValue(value); 69 | } 70 | 71 | if (that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 72 | if (currentPositionCharacteristic.listeners('get').length == 0) { 73 | currentPositionCharacteristic.on("get", function (callback) { 74 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 75 | that.platform.sendReadCommand(deviceSid, command).then(result => { 76 | var value = that.getCurrentPositionCharacteristicValue(result, null); 77 | if (null != value) { 78 | positionStateCharacteristic.updateValue(that.Characteristic.PositionState.STOPPED); 79 | targetPositionCharacteristic.updateValue(value); 80 | callback(null, value); 81 | } else { 82 | callback(new Error('get value fail: ' + result)); 83 | } 84 | }).catch(function (err) { 85 | that.platform.log.error(err); 86 | callback(err); 87 | }); 88 | }); 89 | } 90 | } 91 | 92 | if (targetPositionCharacteristic.listeners('set').length == 0) { 93 | targetPositionCharacteristic.on("set", function (value, callback) { 94 | var model = that.platform.getDeviceModelBySid(deviceSid); 95 | var command = null; 96 | var proto_version_prefix = that.platform.getProtoVersionPrefixByProtoVersion(that.platform.getDeviceProtoVersionBySid(deviceSid)); 97 | if (1 == proto_version_prefix) { 98 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"curtain_level":"' + value + '", "key": "${key}"}}'; 99 | } else if (2 == proto_version_prefix) { 100 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"curtain_level":' + value + '}], "key": "${key}"}'; 101 | } else {} 102 | 103 | if (that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 104 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 105 | that.callback2HB(deviceSid, this, callback, null); 106 | } else { 107 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 108 | that.callback2HB(deviceSid, this, callback, null); 109 | }).catch(function (err) { 110 | that.platform.log.error(err); 111 | that.callback2HB(deviceSid, this, callback, err); 112 | }); 113 | } 114 | }); 115 | } 116 | 117 | that.parserBatteryService(accessory, jsonObj); 118 | } 119 | } 120 | 121 | getCurrentPositionCharacteristicValue(jsonObj, defaultValue) { 122 | var value = this.getValueFrJsonObjData(jsonObj, 'curtain_level'); 123 | if (value / 1.0 > 100) { 124 | return defaultValue; 125 | } else { 126 | return value / 1.0; 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /parser/LockParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class LockParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | var parserInfo = { 11 | 'Lock_MotionSensor': LockMotionSensorParser 12 | }; 13 | 14 | return parserInfo; 15 | } 16 | } 17 | LockParser.modelName = ['lock.aq1']; 18 | module.exports = LockParser; 19 | 20 | class LockMotionSensorParser extends AccessoryParser { 21 | constructor(platform, accessoryType) { 22 | super(platform, accessoryType) 23 | } 24 | 25 | getAccessoryCategory(deviceSid) { 26 | return this.Accessory.Categories.SENSOR; 27 | } 28 | 29 | getAccessoryInformation(deviceSid) { 30 | return { 31 | 'Manufacturer': 'Aqara', 32 | 'Model': 'Lock', 33 | 'SerialNumber': deviceSid 34 | }; 35 | } 36 | 37 | getServices(jsonObj, accessoryName) { 38 | var that = this; 39 | var result = []; 40 | 41 | var deviceSid = jsonObj['sid']; 42 | 43 | var mainMotionSensorService = new that.Service.MotionSensor(accessoryName); 44 | mainMotionSensorService.subtype = 'main'; 45 | mainMotionSensorService.getCharacteristic(that.Characteristic.MotionDetected); 46 | result.push(mainMotionSensorService); 47 | 48 | var batteryService = new that.Service.BatteryService(accessoryName); 49 | batteryService.getCharacteristic(that.Characteristic.StatusLowBattery); 50 | batteryService.getCharacteristic(that.Characteristic.BatteryLevel); 51 | batteryService.getCharacteristic(that.Characteristic.ChargingState); 52 | result.push(batteryService); 53 | 54 | // get Config 55 | var accessoryConfig = that.platform.ConfigUtil.getAccessoryConfig(deviceSid); 56 | if (accessoryConfig) { 57 | for (var key in accessoryConfig) { 58 | if(key.indexOf('Lock_MotionSensor_') > -1) { 59 | var id = key.substring('Lock_MotionSensor_'.length, key.length); 60 | var subMotionSensorName = that.platform.ConfigUtil.getAccessoryName(deviceSid, 'Lock_MotionSensor_' + id); 61 | var subMotionSensorService = new that.Service.MotionSensor(subMotionSensorName); 62 | subMotionSensorService.subtype = id; 63 | result.push(subMotionSensorService); 64 | } 65 | } 66 | } 67 | 68 | return result; 69 | } 70 | 71 | parserAccessories(jsonObj) { 72 | var that = this; 73 | var deviceSid = jsonObj['sid']; 74 | var uuid = that.getAccessoryUUID(deviceSid); 75 | var accessoryName = that.platform.ConfigUtil.getAccessoryName(deviceSid, that.accessoryType); 76 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 77 | if(accessory) { 78 | var mainMotionSensorService = accessory.getServiceByUUIDAndSubType(accessoryName, 'main'); 79 | var mainMotionSensorService = mainMotionSensorService.getCharacteristic(that.Characteristic.MotionDetected); 80 | var value = that.getMotionDetectedCharacteristicValue(jsonObj, null); 81 | if(null != value) { 82 | mainMotionSensorService.updateValue(true); 83 | setTimeout(() => { 84 | mainMotionSensorService.updateValue(false); 85 | }, 1 * 60 * 1000); 86 | 87 | var subMotionSensorName = that.platform.ConfigUtil.getAccessoryName(deviceSid, 'Lock_MotionSensor_' + value); 88 | var subMotionSensorService = accessory.getServiceByUUIDAndSubType(subMotionSensorName, value); 89 | if(subMotionSensorService) { 90 | var subMotionDetectedCharacteristic = subMotionSensorService.getCharacteristic(that.Characteristic.MotionDetected); 91 | subMotionDetectedCharacteristic.updateValue(true); 92 | setTimeout(() => { 93 | subMotionDetectedCharacteristic.updateValue(false); 94 | }, 1 * 60 * 1000); 95 | } 96 | } 97 | /* 98 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 99 | if (motionDetectedCharacteristic.listeners('get').length == 0) { 100 | motionDetectedCharacteristic.on("get", function(callback) { 101 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 102 | that.platform.sendReadCommand(deviceSid, command).then(result => { 103 | var value = that.getMotionDetectedCharacteristicValue(result, null); 104 | if(null != value) { 105 | callback(null, value); 106 | } else { 107 | callback(new Error('get value fail: ' + result)); 108 | } 109 | }).catch(function(err) { 110 | that.platform.log.error(err); 111 | callback(err); 112 | }); 113 | }); 114 | } 115 | } 116 | */ 117 | that.parserBatteryService(accessory, jsonObj); 118 | } 119 | } 120 | 121 | getMotionDetectedCharacteristicValue(jsonObj, defaultValue) { 122 | var success_value = null; 123 | var wrong_value = null; 124 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(jsonObj['sid'])); 125 | if(1 == proto_version_prefix) { 126 | success_value = this.getValueFrJsonObjData1(jsonObj, 'fing_verified') || this.getValueFrJsonObjData1(jsonObj, 'card_verified') || this.getValueFrJsonObjData1(jsonObj, 'psw_verified'); 127 | wrong_value = this.getValueFrJsonObjData1(jsonObj, 'verified_wrong'); 128 | } else if(2 == proto_version_prefix) { 129 | success_value = this.getValueFrJsonObjData2(jsonObj, 'fing_verified') || this.getValueFrJsonObjData2(jsonObj, 'card_verified') || this.getValueFrJsonObjData2(jsonObj, 'psw_verified'); 130 | wrong_value = this.getValueFrJsonObjData2(jsonObj, 'verified_wrong'); 131 | } else { 132 | } 133 | 134 | return wrong_value ? defaultValue : success_value; 135 | } 136 | } -------------------------------------------------------------------------------- /manage/easyui/themes/default/panel.css: -------------------------------------------------------------------------------- 1 | .panel { 2 | overflow: hidden; 3 | text-align: left; 4 | margin: 0; 5 | border: 0; 6 | -moz-border-radius: 0 0 0 0; 7 | -webkit-border-radius: 0 0 0 0; 8 | border-radius: 0 0 0 0; 9 | } 10 | .panel-header, 11 | .panel-body { 12 | border-width: 1px; 13 | border-style: solid; 14 | } 15 | .panel-header { 16 | padding: 5px; 17 | position: relative; 18 | } 19 | .panel-title { 20 | background: url('images/blank.gif') no-repeat; 21 | } 22 | .panel-header-noborder { 23 | border-width: 0 0 1px 0; 24 | } 25 | .panel-body { 26 | overflow: auto; 27 | border-top-width: 0; 28 | padding: 0; 29 | } 30 | .panel-body-noheader { 31 | border-top-width: 1px; 32 | } 33 | .panel-body-noborder { 34 | border-width: 0px; 35 | } 36 | .panel-body-nobottom { 37 | border-bottom-width: 0; 38 | } 39 | .panel-with-icon { 40 | padding-left: 18px; 41 | } 42 | .panel-icon, 43 | .panel-tool { 44 | position: absolute; 45 | top: 50%; 46 | margin-top: -8px; 47 | height: 16px; 48 | overflow: hidden; 49 | } 50 | .panel-icon { 51 | left: 5px; 52 | width: 16px; 53 | } 54 | .panel-tool { 55 | right: 5px; 56 | width: auto; 57 | } 58 | .panel-tool a { 59 | display: inline-block; 60 | width: 16px; 61 | height: 16px; 62 | opacity: 0.6; 63 | filter: alpha(opacity=60); 64 | margin: 0 0 0 2px; 65 | vertical-align: top; 66 | } 67 | .panel-tool a:hover { 68 | opacity: 1; 69 | filter: alpha(opacity=100); 70 | background-color: #eaf2ff; 71 | -moz-border-radius: 3px 3px 3px 3px; 72 | -webkit-border-radius: 3px 3px 3px 3px; 73 | border-radius: 3px 3px 3px 3px; 74 | } 75 | .panel-loading { 76 | padding: 11px 0px 10px 30px; 77 | } 78 | .panel-noscroll { 79 | overflow: hidden; 80 | } 81 | .panel-fit, 82 | .panel-fit body { 83 | height: 100%; 84 | margin: 0; 85 | padding: 0; 86 | border: 0; 87 | overflow: hidden; 88 | } 89 | .panel-loading { 90 | background: url('images/loading.gif') no-repeat 10px 10px; 91 | } 92 | .panel-tool-close { 93 | background: url('images/panel_tools.png') no-repeat -16px 0px; 94 | } 95 | .panel-tool-min { 96 | background: url('images/panel_tools.png') no-repeat 0px 0px; 97 | } 98 | .panel-tool-max { 99 | background: url('images/panel_tools.png') no-repeat 0px -16px; 100 | } 101 | .panel-tool-restore { 102 | background: url('images/panel_tools.png') no-repeat -16px -16px; 103 | } 104 | .panel-tool-collapse { 105 | background: url('images/panel_tools.png') no-repeat -32px 0; 106 | } 107 | .panel-tool-expand { 108 | background: url('images/panel_tools.png') no-repeat -32px -16px; 109 | } 110 | .panel-header, 111 | .panel-body { 112 | border-color: #95B8E7; 113 | } 114 | .panel-header { 115 | background-color: #E0ECFF; 116 | background: -webkit-linear-gradient(top,#EFF5FF 0,#E0ECFF 100%); 117 | background: -moz-linear-gradient(top,#EFF5FF 0,#E0ECFF 100%); 118 | background: -o-linear-gradient(top,#EFF5FF 0,#E0ECFF 100%); 119 | background: linear-gradient(to bottom,#EFF5FF 0,#E0ECFF 100%); 120 | background-repeat: repeat-x; 121 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#EFF5FF,endColorstr=#E0ECFF,GradientType=0); 122 | } 123 | .panel-body { 124 | background-color: #ffffff; 125 | color: #000000; 126 | font-size: 14px; 127 | } 128 | .panel-title { 129 | font-size: 14px; 130 | font-weight: bold; 131 | color: #0E2D5F; 132 | height: 20px; 133 | line-height: 20px; 134 | } 135 | .panel-footer { 136 | border: 1px solid #95B8E7; 137 | overflow: hidden; 138 | background: #F4F4F4; 139 | } 140 | .panel-footer-noborder { 141 | border-width: 1px 0 0 0; 142 | } 143 | .panel-hleft, 144 | .panel-hright { 145 | position: relative; 146 | } 147 | .panel-hleft>.panel-body, 148 | .panel-hright>.panel-body { 149 | position: absolute; 150 | } 151 | .panel-hleft>.panel-header { 152 | float: left; 153 | } 154 | .panel-hright>.panel-header { 155 | float: right; 156 | } 157 | .panel-hleft>.panel-body { 158 | border-top-width: 1px; 159 | border-left-width: 0; 160 | } 161 | .panel-hright>.panel-body { 162 | border-top-width: 1px; 163 | border-right-width: 0; 164 | } 165 | .panel-hleft>.panel-body-nobottom { 166 | border-bottom-width: 1px; 167 | border-right-width: 0; 168 | } 169 | .panel-hright>.panel-body-nobottom { 170 | border-bottom-width: 1px; 171 | border-left-width: 0; 172 | } 173 | .panel-hleft>.panel-footer { 174 | position: absolute; 175 | right: 0; 176 | } 177 | .panel-hright>.panel-footer { 178 | position: absolute; 179 | left: 0; 180 | } 181 | .panel-hleft>.panel-header-noborder { 182 | border-width: 0 1px 0 0; 183 | } 184 | .panel-hright>.panel-header-noborder { 185 | border-width: 0 0 0 1px; 186 | } 187 | .panel-hleft>.panel-body-noborder { 188 | border-width: 0; 189 | } 190 | .panel-hright>.panel-body-noborder { 191 | border-width: 0; 192 | } 193 | .panel-hleft>.panel-body-noheader { 194 | border-left-width: 1px; 195 | } 196 | .panel-hright>.panel-body-noheader { 197 | border-right-width: 1px; 198 | } 199 | .panel-hleft>.panel-footer-noborder { 200 | border-width: 0 0 0 1px; 201 | } 202 | .panel-hright>.panel-footer-noborder { 203 | border-width: 0 1px 0 0; 204 | } 205 | .panel-hleft>.panel-header .panel-icon, 206 | .panel-hright>.panel-header .panel-icon { 207 | margin-top: 0; 208 | top: 5px; 209 | left: 50%; 210 | margin-left: -8px; 211 | } 212 | .panel-hleft>.panel-header .panel-title, 213 | .panel-hright>.panel-header .panel-title { 214 | position: absolute; 215 | min-width: 16px; 216 | left: 25px; 217 | top: 5px; 218 | bottom: auto; 219 | white-space: nowrap; 220 | word-wrap: normal; 221 | -webkit-transform: rotate(90deg); 222 | -webkit-transform-origin: 0 0; 223 | -moz-transform: rotate(90deg); 224 | -moz-transform-origin: 0 0; 225 | -o-transform: rotate(90deg); 226 | -o-transform-origin: 0 0; 227 | transform: rotate(90deg); 228 | transform-origin: 0 0; 229 | } 230 | .panel-hleft>.panel-header .panel-title-up, 231 | .panel-hright>.panel-header .panel-title-up { 232 | position: absolute; 233 | min-width: 16px; 234 | left: 21px; 235 | top: auto; 236 | bottom: 0px; 237 | text-align: right; 238 | white-space: nowrap; 239 | word-wrap: normal; 240 | -webkit-transform: rotate(-90deg); 241 | -webkit-transform-origin: 0 0; 242 | -moz-transform: rotate(-90deg); 243 | -moz-transform-origin: 0 0; 244 | -o-transform: rotate(-90deg); 245 | -o-transform-origin: 0 0; 246 | transform: rotate(-90deg); 247 | transform-origin: 0 16px; 248 | } 249 | .panel-hleft>.panel-header .panel-with-icon.panel-title-up, 250 | .panel-hright>.panel-header .panel-with-icon.panel-title-up { 251 | padding-left: 0; 252 | padding-right: 18px; 253 | } 254 | .panel-hleft>.panel-header .panel-tool, 255 | .panel-hright>.panel-header .panel-tool { 256 | top: auto; 257 | bottom: 5px; 258 | width: 16px; 259 | height: auto; 260 | left: 50%; 261 | margin-left: -8px; 262 | margin-top: 0; 263 | } 264 | .panel-hleft>.panel-header .panel-tool a, 265 | .panel-hright>.panel-header .panel-tool a { 266 | margin: 2px 0 0 0; 267 | } 268 | -------------------------------------------------------------------------------- /manage/easyui/themes/default/datagrid.css: -------------------------------------------------------------------------------- 1 | .datagrid .panel-body { 2 | overflow: hidden; 3 | position: relative; 4 | } 5 | .datagrid-view { 6 | position: relative; 7 | overflow: hidden; 8 | } 9 | .datagrid-view1, 10 | .datagrid-view2 { 11 | position: absolute; 12 | overflow: hidden; 13 | top: 0; 14 | } 15 | .datagrid-view1 { 16 | left: 0; 17 | } 18 | .datagrid-view2 { 19 | right: 0; 20 | } 21 | .datagrid-mask { 22 | position: absolute; 23 | left: 0; 24 | top: 0; 25 | width: 100%; 26 | height: 100%; 27 | opacity: 0.3; 28 | filter: alpha(opacity=30); 29 | display: none; 30 | } 31 | .datagrid-mask-msg { 32 | position: absolute; 33 | top: 50%; 34 | margin-top: -20px; 35 | padding: 10px 5px 10px 30px; 36 | width: auto; 37 | height: 16px; 38 | border-width: 2px; 39 | border-style: solid; 40 | display: none; 41 | } 42 | .datagrid-empty { 43 | position: absolute; 44 | left: 0; 45 | top: 0; 46 | width: 100%; 47 | height: 25px; 48 | line-height: 25px; 49 | text-align: center; 50 | } 51 | .datagrid-sort-icon { 52 | padding: 0; 53 | display: none; 54 | } 55 | .datagrid-toolbar { 56 | height: auto; 57 | padding: 1px 2px; 58 | border-width: 0 0 1px 0; 59 | border-style: solid; 60 | } 61 | .datagrid-btn-separator { 62 | float: left; 63 | height: 24px; 64 | border-left: 1px solid #ccc; 65 | border-right: 1px solid #fff; 66 | margin: 2px 1px; 67 | } 68 | .datagrid .datagrid-pager { 69 | display: block; 70 | margin: 0; 71 | border-width: 1px 0 0 0; 72 | border-style: solid; 73 | } 74 | .datagrid .datagrid-pager-top { 75 | border-width: 0 0 1px 0; 76 | } 77 | .datagrid-header { 78 | overflow: hidden; 79 | cursor: default; 80 | border-width: 0 0 1px 0; 81 | border-style: solid; 82 | } 83 | .datagrid-header-inner { 84 | float: left; 85 | width: 10000px; 86 | } 87 | .datagrid-header-row, 88 | .datagrid-row { 89 | height: 32px; 90 | } 91 | .datagrid-header td, 92 | .datagrid-body td, 93 | .datagrid-footer td { 94 | border-width: 0 1px 1px 0; 95 | border-style: dotted; 96 | margin: 0; 97 | padding: 0; 98 | } 99 | .datagrid-cell, 100 | .datagrid-cell-group, 101 | .datagrid-header-rownumber, 102 | .datagrid-cell-rownumber { 103 | margin: 0; 104 | padding: 0 4px; 105 | white-space: nowrap; 106 | word-wrap: normal; 107 | overflow: hidden; 108 | height: 18px; 109 | line-height: 18px; 110 | font-size: 14px; 111 | } 112 | .datagrid-header .datagrid-cell { 113 | height: auto; 114 | } 115 | .datagrid-header .datagrid-cell span { 116 | font-size: 14px; 117 | } 118 | .datagrid-cell-group { 119 | text-align: center; 120 | text-overflow: ellipsis; 121 | } 122 | .datagrid-header-rownumber, 123 | .datagrid-cell-rownumber { 124 | width: 30px; 125 | text-align: center; 126 | margin: 0; 127 | padding: 0; 128 | } 129 | .datagrid-body { 130 | margin: 0; 131 | padding: 0; 132 | overflow: auto; 133 | zoom: 1; 134 | } 135 | .datagrid-view1 .datagrid-body-inner { 136 | padding-bottom: 20px; 137 | } 138 | .datagrid-view1 .datagrid-body { 139 | overflow: hidden; 140 | } 141 | .datagrid-footer { 142 | overflow: hidden; 143 | } 144 | .datagrid-footer-inner { 145 | border-width: 1px 0 0 0; 146 | border-style: solid; 147 | width: 10000px; 148 | float: left; 149 | } 150 | .datagrid-row-editing .datagrid-cell { 151 | height: auto; 152 | } 153 | .datagrid-header-check, 154 | .datagrid-cell-check { 155 | padding: 0; 156 | width: 27px; 157 | height: 18px; 158 | font-size: 1px; 159 | text-align: center; 160 | overflow: hidden; 161 | } 162 | .datagrid-header-check input, 163 | .datagrid-cell-check input { 164 | margin: 0; 165 | padding: 0; 166 | width: 15px; 167 | height: 18px; 168 | } 169 | .datagrid-resize-proxy { 170 | position: absolute; 171 | width: 1px; 172 | height: 10000px; 173 | top: 0; 174 | cursor: e-resize; 175 | display: none; 176 | } 177 | .datagrid-body .datagrid-editable { 178 | margin: 0; 179 | padding: 0; 180 | } 181 | .datagrid-body .datagrid-editable table { 182 | width: 100%; 183 | height: 100%; 184 | } 185 | .datagrid-body .datagrid-editable td { 186 | border: 0; 187 | margin: 0; 188 | padding: 0; 189 | } 190 | .datagrid-view .datagrid-editable-input { 191 | margin: 0; 192 | padding: 2px 4px; 193 | border: 1px solid #95B8E7; 194 | font-size: 14px; 195 | outline-style: none; 196 | -moz-border-radius: 0 0 0 0; 197 | -webkit-border-radius: 0 0 0 0; 198 | border-radius: 0 0 0 0; 199 | } 200 | .datagrid-view .validatebox-invalid { 201 | border-color: #ffa8a8; 202 | } 203 | .datagrid-sort .datagrid-sort-icon { 204 | display: inline; 205 | padding: 0 13px 0 0; 206 | background: url('images/datagrid_icons.png') no-repeat -64px center; 207 | } 208 | .datagrid-sort-desc .datagrid-sort-icon { 209 | display: inline; 210 | padding: 0 13px 0 0; 211 | background: url('images/datagrid_icons.png') no-repeat -16px center; 212 | } 213 | .datagrid-sort-asc .datagrid-sort-icon { 214 | display: inline; 215 | padding: 0 13px 0 0; 216 | background: url('images/datagrid_icons.png') no-repeat 0px center; 217 | } 218 | .datagrid-row-collapse { 219 | background: url('images/datagrid_icons.png') no-repeat -48px center; 220 | } 221 | .datagrid-row-expand { 222 | background: url('images/datagrid_icons.png') no-repeat -32px center; 223 | } 224 | .datagrid-mask-msg { 225 | background: #ffffff url('images/loading.gif') no-repeat scroll 5px center; 226 | } 227 | .datagrid-header, 228 | .datagrid-td-rownumber { 229 | background-color: #efefef; 230 | background: -webkit-linear-gradient(top,#F9F9F9 0,#efefef 100%); 231 | background: -moz-linear-gradient(top,#F9F9F9 0,#efefef 100%); 232 | background: -o-linear-gradient(top,#F9F9F9 0,#efefef 100%); 233 | background: linear-gradient(to bottom,#F9F9F9 0,#efefef 100%); 234 | background-repeat: repeat-x; 235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#F9F9F9,endColorstr=#efefef,GradientType=0); 236 | } 237 | .datagrid-cell-rownumber { 238 | color: #000000; 239 | } 240 | .datagrid-resize-proxy { 241 | background: #aac5e7; 242 | } 243 | .datagrid-mask { 244 | background: #ccc; 245 | } 246 | .datagrid-mask-msg { 247 | border-color: #95B8E7; 248 | } 249 | .datagrid-toolbar, 250 | .datagrid-pager { 251 | background: #F4F4F4; 252 | } 253 | .datagrid-header, 254 | .datagrid-toolbar, 255 | .datagrid-pager, 256 | .datagrid-footer-inner { 257 | border-color: #dddddd; 258 | } 259 | .datagrid-header td, 260 | .datagrid-body td, 261 | .datagrid-footer td { 262 | border-color: #ccc; 263 | } 264 | .datagrid-htable, 265 | .datagrid-btable, 266 | .datagrid-ftable { 267 | color: #000000; 268 | border-collapse: separate; 269 | } 270 | .datagrid-row-alt { 271 | background: #fafafa; 272 | } 273 | .datagrid-row-over, 274 | .datagrid-header td.datagrid-header-over { 275 | background: #eaf2ff; 276 | color: #000000; 277 | cursor: default; 278 | } 279 | .datagrid-row-selected { 280 | background: #ffe48d; 281 | color: #000000; 282 | } 283 | .datagrid-row-editing .textbox, 284 | .datagrid-row-editing .textbox-text { 285 | -moz-border-radius: 0 0 0 0; 286 | -webkit-border-radius: 0 0 0 0; 287 | border-radius: 0 0 0 0; 288 | } 289 | .datagrid-header .datagrid-filter-row td.datagrid-header-over { 290 | background: inherit; 291 | } 292 | -------------------------------------------------------------------------------- /parser/DuplexSwitchParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class DuplexSwitchParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'DuplexSwitch_Switch_Left': DuplexSwitchSwitchLeftParser, 12 | 'DuplexSwitch_Switch_Right': DuplexSwitchSwitchRightParser 13 | } 14 | } 15 | } 16 | DuplexSwitchParser.modelName = ['ctrl_neutral2']; 17 | module.exports = DuplexSwitchParser; 18 | 19 | class DuplexSwitchSwitchBaseParser extends AccessoryParser { 20 | constructor(platform, accessoryType) { 21 | super(platform, accessoryType) 22 | } 23 | 24 | getAccessoryCategory(deviceSid) { 25 | var serviceType = this.platform.ConfigUtil.getAccessoryServiceType(deviceSid, this.accessoryType); 26 | if(serviceType == 'Lightbulb') { 27 | return this.Accessory.Categories.LIGHTBULB; 28 | } else { 29 | return this.Accessory.Categories.SWITCH; 30 | } 31 | } 32 | 33 | getAccessoryInformation(deviceSid) { 34 | return { 35 | 'Manufacturer': 'Aqara', 36 | 'Model': 'Duplex Switch', 37 | 'SerialNumber': deviceSid 38 | }; 39 | } 40 | 41 | getServices(jsonObj, accessoryName) { 42 | var that = this; 43 | var result = []; 44 | 45 | var service = null; 46 | var deviceSid = jsonObj['sid']; 47 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 48 | if(serviceType == 'Lightbulb') { 49 | service = new that.Service.Lightbulb(accessoryName); 50 | } else { 51 | service = new that.Service.Switch(accessoryName); 52 | } 53 | service.getCharacteristic(that.Characteristic.On); 54 | result.push(service); 55 | 56 | return result; 57 | } 58 | 59 | parserAccessories(jsonObj) { 60 | var that = this; 61 | var deviceSid = jsonObj['sid']; 62 | var uuid = that.getAccessoryUUID(deviceSid); 63 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 64 | if(accessory) { 65 | var service = null; 66 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 67 | if(serviceType == 'Lightbulb') { 68 | service = accessory.getService(that.Service.Lightbulb); 69 | } else { 70 | service = accessory.getService(that.Service.Switch); 71 | } 72 | var onCharacteristic = service.getCharacteristic(that.Characteristic.On); 73 | var value = that.getOnCharacteristicValue(jsonObj, null); 74 | if(null != value) { 75 | onCharacteristic.updateValue(value); 76 | } 77 | 78 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 79 | if (onCharacteristic.listeners('get').length == 0) { 80 | onCharacteristic.on("get", function(callback) { 81 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 82 | that.platform.sendReadCommand(deviceSid, command).then(result => { 83 | var value = that.getOnCharacteristicValue(result, null); 84 | if(null != value) { 85 | callback(null, value); 86 | } else { 87 | callback(new Error('get value fail: ' + result)); 88 | } 89 | }).catch(function(err) { 90 | that.platform.log.error(err); 91 | callback(err); 92 | }); 93 | }); 94 | } 95 | } 96 | 97 | if(onCharacteristic.listeners('set').length == 0) { 98 | onCharacteristic.on("set", function(value, callback) { 99 | var command = that.getWriteCommand(deviceSid, value); 100 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 101 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 102 | that.callback2HB(deviceSid, this, callback, null); 103 | } else { 104 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 105 | that.callback2HB(deviceSid, this, callback, null); 106 | }).catch(function(err) { 107 | that.platform.log.error(err); 108 | that.callback2HB(deviceSid, this, callback, err); 109 | }); 110 | } 111 | }); 112 | } 113 | } 114 | } 115 | } 116 | 117 | class DuplexSwitchSwitchLeftParser extends DuplexSwitchSwitchBaseParser { 118 | getOnCharacteristicValue(jsonObj, defaultValue) { 119 | var value = this.getValueFrJsonObjData(jsonObj, 'channel_0'); 120 | if(value === 'on') { 121 | return true; 122 | } else if(value === 'off') { 123 | return false; 124 | } else { 125 | return defaultValue; 126 | } 127 | } 128 | 129 | getWriteCommand(deviceSid, value) { 130 | var model = this.platform.getDeviceModelBySid(deviceSid); 131 | var command = null; 132 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(deviceSid)); 133 | if(1 == proto_version_prefix) { 134 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"channel_0":"' + (value ? 'on' : 'off') + '", "key": "${key}"}}'; 135 | } else if(2 == proto_version_prefix) { 136 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_0":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 137 | } else { 138 | } 139 | 140 | return command; 141 | } 142 | } 143 | 144 | class DuplexSwitchSwitchRightParser extends DuplexSwitchSwitchBaseParser { 145 | getOnCharacteristicValue(jsonObj, defaultValue) { 146 | var value = this.getValueFrJsonObjData(jsonObj, 'channel_1'); 147 | if(value === 'on') { 148 | return true; 149 | } else if(value === 'off') { 150 | return false; 151 | } else { 152 | return defaultValue; 153 | } 154 | } 155 | 156 | getWriteCommand(deviceSid, value) { 157 | var model = this.platform.getDeviceModelBySid(deviceSid); 158 | var command = null; 159 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(deviceSid)); 160 | if(1 == proto_version_prefix) { 161 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"channel_1":"' + (value ? 'on' : 'off') + '", "key": "${key}"}"}'; 162 | } else if(2 == proto_version_prefix) { 163 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_1":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 164 | } else { 165 | } 166 | return command; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /parser/DuplexSwitchLNParser.js: -------------------------------------------------------------------------------- 1 | const DeviceParser = require('./DeviceParser'); 2 | const AccessoryParser = require('./AccessoryParser'); 3 | 4 | class DuplexSwitchLNParser extends DeviceParser { 5 | constructor(platform) { 6 | super(platform); 7 | } 8 | 9 | getAccessoriesParserInfo() { 10 | return { 11 | 'DuplexSwitchLN_Switch_Left': DuplexSwitchLNSwitchLeftParser, 12 | 'DuplexSwitchLN_Switch_Right': DuplexSwitchLNSwitchRightParser 13 | } 14 | } 15 | } 16 | DuplexSwitchLNParser.modelName = ['ctrl_ln2', 'ctrl_ln2.aq1']; 17 | module.exports = DuplexSwitchLNParser; 18 | 19 | class DuplexSwitchLNSwitchBaseParser extends AccessoryParser { 20 | constructor(platform, accessoryType) { 21 | super(platform, accessoryType) 22 | } 23 | 24 | getAccessoryCategory(deviceSid) { 25 | var serviceType = this.platform.ConfigUtil.getAccessoryServiceType(deviceSid, this.accessoryType); 26 | if(serviceType == 'Lightbulb') { 27 | return this.Accessory.Categories.LIGHTBULB; 28 | } else { 29 | return this.Accessory.Categories.SWITCH; 30 | } 31 | } 32 | 33 | getAccessoryInformation(deviceSid) { 34 | return { 35 | 'Manufacturer': 'Aqara', 36 | 'Model': 'Duplex Switch LN', 37 | 'SerialNumber': deviceSid 38 | }; 39 | } 40 | 41 | getServices(jsonObj, accessoryName) { 42 | var that = this; 43 | var result = []; 44 | 45 | var service = null; 46 | var deviceSid = jsonObj['sid']; 47 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 48 | if(serviceType == 'Lightbulb') { 49 | service = new that.Service.Lightbulb(accessoryName); 50 | } else { 51 | service = new that.Service.Switch(accessoryName); 52 | } 53 | service.getCharacteristic(that.Characteristic.On); 54 | result.push(service); 55 | 56 | return result; 57 | } 58 | 59 | parserAccessories(jsonObj) { 60 | var that = this; 61 | var deviceSid = jsonObj['sid']; 62 | var uuid = that.getAccessoryUUID(deviceSid); 63 | var accessory = that.platform.AccessoryUtil.getByUUID(uuid); 64 | if(accessory) { 65 | var service = null; 66 | var serviceType = that.platform.ConfigUtil.getAccessoryServiceType(deviceSid, that.accessoryType); 67 | if(serviceType == 'Lightbulb') { 68 | service = accessory.getService(that.Service.Lightbulb); 69 | } else { 70 | service = accessory.getService(that.Service.Switch); 71 | } 72 | var onCharacteristic = service.getCharacteristic(that.Characteristic.On); 73 | var value = that.getOnCharacteristicValue(jsonObj, null); 74 | if(null != value) { 75 | onCharacteristic.updateValue(value); 76 | } 77 | 78 | if(that.platform.ConfigUtil.getAccessorySyncValue(deviceSid, that.accessoryType)) { 79 | if (onCharacteristic.listeners('get').length == 0) { 80 | onCharacteristic.on("get", function(callback) { 81 | var command = '{"cmd":"read", "sid":"' + deviceSid + '"}'; 82 | that.platform.sendReadCommand(deviceSid, command).then(result => { 83 | var value = that.getOnCharacteristicValue(result, null); 84 | if(null != value) { 85 | callback(null, value); 86 | } else { 87 | callback(new Error('get value fail: ' + result)); 88 | } 89 | }).catch(function(err) { 90 | that.platform.log.error(err); 91 | callback(err); 92 | }); 93 | }); 94 | } 95 | } 96 | 97 | if(onCharacteristic.listeners('set').length == 0) { 98 | onCharacteristic.on("set", function(value, callback) { 99 | var command = that.getWriteCommand(deviceSid, value); 100 | if(that.platform.ConfigUtil.getAccessoryIgnoreWriteResult(deviceSid, that.accessoryType)) { 101 | that.platform.sendWriteCommandWithoutFeedback(deviceSid, command); 102 | that.callback2HB(deviceSid, this, callback, null); 103 | } else { 104 | that.platform.sendWriteCommand(deviceSid, command).then(result => { 105 | that.callback2HB(deviceSid, this, callback, null); 106 | }).catch(function(err) { 107 | that.platform.log.error(err); 108 | that.callback2HB(deviceSid, this, callback, err); 109 | }); 110 | } 111 | }); 112 | } 113 | } 114 | } 115 | } 116 | 117 | class DuplexSwitchLNSwitchLeftParser extends DuplexSwitchLNSwitchBaseParser { 118 | getOnCharacteristicValue(jsonObj, defaultValue) { 119 | var value = this.getValueFrJsonObjData(jsonObj, 'channel_0'); 120 | if(value === 'on') { 121 | return true; 122 | } else if(value === 'off') { 123 | return false; 124 | } else { 125 | return defaultValue; 126 | } 127 | } 128 | 129 | getWriteCommand(deviceSid, value) { 130 | var model = this.platform.getDeviceModelBySid(deviceSid); 131 | var command = null; 132 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(deviceSid)); 133 | if(1 == proto_version_prefix) { 134 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"channel_0":"' + (value ? 'on' : 'off') + '", "key": "${key}"}}'; 135 | } else if(2 == proto_version_prefix) { 136 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_0":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 137 | } else { 138 | } 139 | 140 | return command; 141 | } 142 | } 143 | 144 | class DuplexSwitchLNSwitchRightParser extends DuplexSwitchLNSwitchBaseParser { 145 | getOnCharacteristicValue(jsonObj, defaultValue) { 146 | var value = this.getValueFrJsonObjData(jsonObj, 'channel_1'); 147 | if(value === 'on') { 148 | return true; 149 | } else if(value === 'off') { 150 | return false; 151 | } else { 152 | return defaultValue; 153 | } 154 | } 155 | 156 | getWriteCommand(deviceSid, value) { 157 | var model = this.platform.getDeviceModelBySid(deviceSid); 158 | var command = null; 159 | var proto_version_prefix = this.platform.getProtoVersionPrefixByProtoVersion(this.platform.getDeviceProtoVersionBySid(deviceSid)); 160 | if(1 == proto_version_prefix) { 161 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","data":{"channel_1":"' + (value ? 'on' : 'off') + '", "key": "${key}"}}'; 162 | } else if(2 == proto_version_prefix) { 163 | command = '{"cmd":"write","model":"' + model + '","sid":"' + deviceSid + '","params":[{"channel_1":"' + (value ? 'on' : 'off') + '"}], "key": "${key}"}'; 164 | } else { 165 | } 166 | 167 | return command; 168 | } 169 | } 170 | --------------------------------------------------------------------------------