├── userdata └── empty_folder ├── .DS_Store ├── lib ├── .DS_Store └── homekit.js ├── assets ├── .DS_Store ├── images │ ├── .DS_Store │ ├── large.png │ └── small.png └── icon.svg ├── settings ├── .DS_Store ├── code.png └── index.html ├── locales ├── en.json └── nl.json ├── package.json ├── app.json ├── api.js ├── README.md └── app.js /userdata/empty_folder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/.DS_Store -------------------------------------------------------------------------------- /lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/lib/.DS_Store -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/assets/.DS_Store -------------------------------------------------------------------------------- /settings/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/settings/.DS_Store -------------------------------------------------------------------------------- /settings/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/settings/code.png -------------------------------------------------------------------------------- /assets/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/assets/images/.DS_Store -------------------------------------------------------------------------------- /assets/images/large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/assets/images/large.png -------------------------------------------------------------------------------- /assets/images/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sprut666666/com.sprut.homekit/HEAD/assets/images/small.png -------------------------------------------------------------------------------- /locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "tabs": { 4 | "devices": "Devices", 5 | "log": "Log" 6 | }, 7 | "device": { 8 | "add": "Add", 9 | "delete": "Delete" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /locales/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "tabs": { 4 | "devices": "Apparaten", 5 | "log": "Log" 6 | }, 7 | "device": { 8 | "add": "Toevoegen", 9 | "delete": "Verwijderen" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.sprut.homekit", 3 | "version": "1.5.1", 4 | "description": "Homekit app for Homey", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Bas Jansen & Sprut", 10 | "license": "ISC", 11 | "dependencies": { 12 | "athom-api": "^2.0.108", 13 | "has-node": "^0.4.13", 14 | "new-types-for-homekit": "git+https://github.com/sprut666666/new-types-for-homekit.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "com.sprut.homekit", 3 | "sdk": 2, 4 | "permissions": [ 5 | "homey:manager:api" 6 | ], 7 | "name": { 8 | "en": "HomeKit", 9 | "nl": "HomeKit" 10 | }, 11 | "description": { 12 | "en": "Homekit support for Homey", 13 | "nl": "Homekit ondersteuning voor Homey" 14 | }, 15 | "tags": { 16 | "en": ["homekit", "siri", "iOS"], 17 | "nl": ["homekit", "siri", "iOS"] 18 | }, 19 | "category": "appliances", 20 | "version": "1.5.1", 21 | "compatibility": ">=1.5.0", 22 | "author": { 23 | "name": "Sprut", 24 | "email": "sprut.1@me.com" 25 | }, 26 | "contributors": { 27 | "developers": [ 28 | { 29 | "name": "Sprut", 30 | "email": "sprut.1@me.com" 31 | }, 32 | { 33 | "name": "Bas Jansen", 34 | "email": "b@sjansen.email" 35 | }, 36 | { 37 | "name": "Abedinpour", 38 | "email": "abedinpourmh@gmail.com" 39 | } 40 | ] 41 | }, 42 | "images": { 43 | "large": "./assets/images/large.png", 44 | "small": "./assets/images/small.png" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Homey = require('homey') 3 | 4 | module.exports = [ 5 | 6 | { 7 | method: 'GET', 8 | path: '/devices', 9 | fn: function(args, callback) { 10 | Homey.app.getDevices().then(res => { 11 | callback(null, res); 12 | }) 13 | .catch(error => callback(error, null)); 14 | 15 | } 16 | }, 17 | { 18 | method: 'GET', 19 | path: '/log', 20 | fn: function(args, callback) { 21 | callback(null, Homey.app.getLog()); 22 | 23 | } 24 | }, 25 | { 26 | method: 'PUT', 27 | path: '/devices/add', 28 | fn: function(args, callback) { 29 | Homey.app.addDevice(args.body).then(res => { 30 | callback(null, true); 31 | }) 32 | .catch(error => callback(error, null)); 33 | 34 | } 35 | }, 36 | { 37 | method: 'DELETE', 38 | path: '/devices/delete', 39 | fn: function(args, callback) { 40 | console.log('API call received, trying to remove ' + args.body.name, 'info'); 41 | Homey.app.deleteDevice(args.body).then(res => { 42 | callback(null, true); 43 | }) 44 | .catch(error => { 45 | console.log(err, 'error') 46 | callback(error, null); 47 | }); 48 | 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /assets/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HomeKit for Homey 2 | 3 | ### Support for new exclusive services: [HomeCenter for HomeKit](https://itunes.apple.com/ru/app/homecenter-for-homekit/id1329662539?mt=8) 4 | ![ScreenShot](https://github.com/sprut666666/graphics/blob/master/homecenter/ScreenShot.png) 5 | 6 | ![Image of paircode](https://github.com/sprut666666/graphics/blob/master/homekit/code.png) 7 | 8 | After Homey is paired, go to "settings" -> "HomeKit". There select the devices you want to pair with "HomeKit" and wait =) 9 | Example of work: https://www.youtube.com/watch?v=yZWt6jDCl7E (New video from Homey the work) 10 | 11 | Forum => https://forum.athom.com/discussion/3958/app-homekit-app-sprut 12 | 13 | Remote access in HomeKit: https://support.apple.com/en-us/HT207057 14 | 15 | If you have problems update your "I" device & Apple TV. On iOS 11 everything works perfectly. If you don't see for example the "SPEAKER" look here https://itunes.apple.com/us/app/elgato-eve/id917695792?mt=8 16 | 17 | If the problem remained fully describe the situation. If you found any bugs, any other feature you can create an issue on [com.sprut.homekit](https://github.com/sprut666666/com.sprut.homekit) 18 | 19 | You can add any device and if it supported device types they will be added to HomeKit. If the device is not supported device types will be added to the device "NOT SUPPORTED" - If you want I added a new device type send me "full info:" the device from the log on sprut666666@gmail.com 20 | 21 | Now supports the types: 22 | - Light (On-off, dim, Temperature control, RGB) 23 | - Fan 24 | - Switch 25 | - Outlet 26 | - Doorlock 27 | - Curtains 28 | - Motion sensor 29 | - Humidity sensor 30 | - Light sensor 31 | - Carbon dioxide sensor 32 | - Temperature sensor 33 | - Leak sensor 34 | - Smoke sensor 35 | - Contact sensor (door/window sensor) 36 | - AirQuality sensor 37 | - Thermostat 38 | - Volume speaker 39 | - Vacuum cleaner 40 | - Button (simple and Play/Pause etc) 41 | - Doorbell button (as Motion sensor) 42 | - Occupancy sensor 43 | - Atmospheric Pressure sensor 44 | - Noise Level sensor 45 | - Ultraviolet sensor 46 | - Power Meter sensor 47 | - Security System sensor 48 | 49 | + Battery service for all 50 | 51 | --- 52 | 53 | ### About 54 | Many thanks to the developer who wrote the library [has-node](https://github.com/abedinpour/HAS) Without which the application cannot run ;) 55 | Many thanks [abedinpour](https://github.com/abedinpour) so much for the work done. 56 | 57 | The basis of this application is taken development [com.swttt.homekit](https://github.com/swttt/com.swttt.homekit) 58 | Many thanks [Swttt](https://github.com/swttt) so much for the work done. 59 | 60 | And I [Sprut](https://github.com/sprut666666) - engaged in ongoing app development =) 61 | 62 | --- 63 | 64 | ### Changelog 65 | 66 | #### 1.5.1 67 | - critical fix 68 | 69 | #### 1.5.0 70 | - update has-node@0.4.13 71 | - update athom-api@2.0.108 72 | - Support temperature is less than zero 73 | - Fix Error: Homey Offline 74 | - Add lib new-types-for-homekit@1.0.1 75 | - Add support Atmospheric Pressure 76 | - Add support Noise Level 77 | - Add support Ultraviolet 78 | - Add support Power Meter 79 | - Add support Security System 80 | 81 | #### 1.4.0 82 | - update has-node@0.4.11 83 | - update athom-api@2.0.93 84 | - Now you can add 150 devices 85 | - Memory optimization 86 | 87 | #### 1.3.5 88 | - update has-node@0.4.9 89 | - critical fixes 90 | 91 | #### 1.3.3 92 | - update has-node@0.4.8 93 | 94 | #### 1.3.2 95 | - update has-node@0.4.6 96 | - update athom-api@2.0.92 97 | - fix "No response" 98 | 99 | #### 1.3.1 100 | - update has-node@0.4.5 101 | - update athom-api@2.0.91 102 | 103 | #### 1.2.4 104 | - update has-node@0.3.4 105 | 106 | #### 1.2.3 107 | - update has-node@0.3.3 108 | - update athom-api@2.0.72 109 | 110 | #### 1.2.2 111 | - update has-node@0.3.1 112 | - update athom-api@2.0.71 113 | - fix delete device 114 | 115 | #### 1.2.1 116 | - update has-node@0.2.6 117 | - update athom-api@2.0.61 118 | 119 | #### 1.2.0 120 | - Fix & add OccupancySensor 121 | - update has-node@0.2.5 122 | - update athom-api@2.0.37 123 | 124 | #### 1.1.9 125 | - Fix README 126 | 127 | #### 1.1.8 128 | - Fix info & README 129 | 130 | #### 1.1.7 131 | - If wakeup interval > 15 seconds - no online state 132 | 133 | #### 1.1.6 134 | - Extended support for status updates of devices 135 | 136 | #### 1.1.5 137 | - Verification of successful installation of the new parameters in Homey 138 | - Fix for MiLight 139 | 140 | #### 1.1.4 141 | - Small fixes 142 | 143 | #### 1.1.3 144 | - Critical bugfix when adding many devices 145 | 146 | #### 1.1.2 147 | - update has-node 0.2.3 148 | 149 | #### 1.1.1 150 | - fix WindowCovering 151 | 152 | #### 1.1.0 153 | - update has-node 0.2.2 154 | - new ColorTemperature 155 | - fix RGBW 156 | 157 | #### 1.0.5 158 | - fix bugs in Thermostat & expansion of functionality 159 | 160 | #### 1.0.4 161 | - fix Thermostat not measure_temperature 162 | 163 | #### 1.0.3 164 | - Add Doorbell button (as Motion sensor) 165 | 166 | #### 1.0.2 167 | - Add full device info for debug 168 | 169 | #### 1.0.1 170 | - Support 2 bridges for example com.swttt.homekit 171 | 172 | #### 1.0.0 173 | - Initial release 174 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const debug = true; 4 | 5 | // Enable TCP debug 6 | // process.env.DEBUG = 'TCP'; 7 | 8 | const Homey = require('homey') 9 | const { HomeyAPI } = require('athom-api') 10 | const Homekit = require('./lib/homekit.js') 11 | 12 | 13 | 14 | let allDevices = {}, 15 | allPairedDevices = [], 16 | server = {}, 17 | log = []; 18 | 19 | if (debug) { 20 | console.log = function(string, type) { 21 | let d = new Date(); 22 | let n = d.toLocaleTimeString(); 23 | let item = {}; 24 | item.time = n; 25 | item.string = string; 26 | item.type = type; 27 | log.push(item); 28 | Homey.ManagerApi.realtime('log.new', log) 29 | .catch( this.error ) 30 | if(log.length > 50){ 31 | log.splice(0,1); 32 | } 33 | }; 34 | } 35 | 36 | 37 | class HomekitApp extends Homey.App 38 | { 39 | // Get homey object 40 | getApi() { 41 | if (!this.api) { 42 | this.api = HomeyAPI.forCurrentHomey(); 43 | } 44 | return this.api; 45 | } 46 | async getDevices() { 47 | let api = await this.getApi(); 48 | allDevices = await api.devices.getDevices(); 49 | return allDevices; 50 | } 51 | 52 | getLog() { 53 | return log; 54 | } 55 | 56 | // Start server function 57 | async startingServer() 58 | { 59 | // Get the homey object 60 | let api = await this.getApi(); 61 | // Get system info 62 | let systeminfo = await api.system.getInfo(); 63 | // Subscribe to realtime events and set all devices global 64 | await api.devices.subscribe(); 65 | allDevices = await api.devices.getDevices(); 66 | 67 | server = await Homekit.configServer(systeminfo); 68 | 69 | // Loop all devices 70 | allPairedDevices = await Homey.ManagerSettings.get('pairedDevices') || []; 71 | let deviceForDel = []; 72 | 73 | for (let i = 0; i < allPairedDevices.length; i++) 74 | { 75 | // If device has the class light 76 | let device = allPairedDevices[i]; 77 | 78 | if (device.id in allDevices) 79 | { 80 | console.log(device.name + ' - device found.', 'info'); 81 | await this.addDevice(device,true); 82 | } 83 | else 84 | { 85 | console.log(device.name + ' - device not found.', 'info'); 86 | deviceForDel.push(device); 87 | } 88 | } 89 | 90 | if (deviceForDel.length) 91 | { 92 | for (let i = 0; i < deviceForDel.length; i++) 93 | { 94 | let device = deviceForDel[i]; 95 | 96 | for (let i = 0; i < allPairedDevices.length; i++) 97 | { 98 | if (allPairedDevices[i] && allPairedDevices[i].id == device.id) 99 | { 100 | allPairedDevices.splice(i, 1); 101 | break; 102 | } 103 | } 104 | } 105 | 106 | await Homey.ManagerSettings.set('pairedDevices', allPairedDevices, (err, result) => 107 | { 108 | if (err) 109 | return Homey.alert(err); 110 | }); 111 | 112 | console.log('Delete paired devices! => ' + deviceForDel.length, 'success'); 113 | } 114 | 115 | if(allPairedDevices.length) 116 | { 117 | await console.log('Added all devices..done here!', 'success'); 118 | } 119 | else{ 120 | await console.log('No devices found...', 'info'); 121 | } 122 | 123 | // Start the server 124 | await server.startServer(); 125 | console.log('Homekit server started.', 'success'); 126 | 127 | api.devices.on('device.delete', deviceID => 128 | { 129 | allPairedDevices = Homey.ManagerSettings.get('pairedDevices') || []; 130 | 131 | let deletePairedDevices = false; 132 | 133 | for (let i = 0; i < allPairedDevices.length; i++) 134 | { 135 | if (allPairedDevices[i] && allPairedDevices[i].id == deviceID) 136 | { 137 | allPairedDevices.splice(i, 1); 138 | deletePairedDevices = true; 139 | break; 140 | } 141 | } 142 | 143 | if (deletePairedDevices) 144 | { 145 | server.removeAccessory(server.config.getHASID(deviceID)); 146 | 147 | Homey.ManagerSettings.set('pairedDevices', allPairedDevices, (err, result) => 148 | { 149 | if (err) 150 | return Homey.alert(err); 151 | }); 152 | 153 | console.log('Delete device! => ' + deviceID, 'success'); 154 | 155 | } 156 | 157 | }); 158 | 159 | } 160 | 161 | async onInit() 162 | { 163 | // Start the server 164 | await this.startingServer() 165 | .then(console.log('Homekit server starting!', 'info')) 166 | .catch(this.error); 167 | } 168 | 169 | 170 | 171 | async addDevice(device,noCheck) 172 | { 173 | if (noCheck === undefined) 174 | { 175 | await this.getDevices(); 176 | console.log(device.name + ' getDevices() ', 'info'); 177 | } 178 | 179 | console.log(device.name + ' class: ' + allDevices[device.id].class, 'info'); 180 | 181 | let light = await Homekit.createDevice(allDevices[device.id], server.config.getHASID(device.id)); 182 | await server.addAccessory(light); 183 | 184 | console.log(device.name + ' is added!', 'success'); 185 | 186 | } 187 | 188 | async deleteDevice(device) 189 | { 190 | console.log('Trying to remove device ' + device.id, "info"); 191 | 192 | allDevices[device.id].removeAllListeners('$state'); 193 | server.removeAccessory(server.config.getHASID(device.id)); 194 | server.config.resetHASID(device.id); 195 | 196 | console.log(device.name + ' is removed!', 'success'); 197 | } 198 | 199 | } 200 | 201 | module.exports = HomekitApp 202 | -------------------------------------------------------------------------------- /settings/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 23 |
24 | 25 |
26 | 27 |
28 |
29 |
30 |
31 | {{device.name}}
32 | {{device.zone.name}}
33 | Add 34 | Delete 35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 | 43 | 44 | 45 | 51 | 52 |
{{log.time}} 46 | Info 47 | Success 48 | Error 49 | {{log.string}} 50 |
53 |
54 |
55 | 56 |
57 | 58 | 59 | 60 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /lib/homekit.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const HAS = require('has-node'); 3 | const NewTypes = require('new-types-for-homekit/has.js'); 4 | 5 | 6 | 7 | function configServer(homey) 8 | { 9 | function reverseStr(str) 10 | { 11 | return str.split("").reverse().join(""); 12 | } 13 | 14 | let server = {}; 15 | 16 | // Config for server 17 | let config = new HAS.Config(homey.hostname + ' ' + homey.cloud_id, reverseStr(homey.wifi_mac), HAS.categories.bridge, '../userdata/homey.json', 8091, '666-66-666'); 18 | //let config = new HAS.Config(homey.hostname, homey.wifi_mac, HAS.categories.bridge, '../userdata/homey.json', 8099, '666-66-666'); 19 | 20 | server = new HAS.Server(config); 21 | 22 | // Create bridge 23 | server.config.getHASID(homey.wifi_mac); 24 | let bridge = new HAS.Accessory(1); 25 | 26 | // What happens when a user presses identify in the Home app (Idea: add speech output?) 27 | let identify = HAS.predefined.Identify(1, undefined, function(value, callback) { 28 | callback(HAS.statusCodes.OK); 29 | }); 30 | 31 | // Set device information for the bridge 32 | let manufacturer = HAS.predefined.Manufacturer(2, 'Athom'); 33 | let model = HAS.predefined.Model(3, 'V1'); 34 | let name = HAS.predefined.Name(4, homey.hostname); 35 | let serialNumber = HAS.predefined.SerialNumber(5, homey.cloud_id); 36 | let firmwareVersion = HAS.predefined.FirmwareRevision(6, homey.homey_version); 37 | 38 | // Add all services to the created bridge accesory 39 | bridge.addServices(HAS.predefined.AccessoryInformation(1, [identify, manufacturer, model, name, serialNumber, firmwareVersion])); 40 | 41 | // Add bridge to the server 42 | server.addAccessory(bridge); 43 | server.onIdentify = identify.onWrite; 44 | console.log('Server config done.', 'success'); 45 | // Return server to app.js 46 | return server; 47 | } 48 | 49 | 50 | function map(inputStart, inputEnd, outputStart, outputEnd, input) 51 | { 52 | return outputStart + ((outputEnd - outputStart) / (inputEnd - inputStart)) * (input - inputStart); 53 | } 54 | 55 | function state2num(state) 56 | { 57 | if (state) { 58 | return 1; 59 | } 60 | else { 61 | return 0; 62 | } 63 | } 64 | 65 | function stateCover2num(state) 66 | { 67 | if (state=='up') 68 | { 69 | return 1; 70 | } 71 | else if(state=='down') 72 | { 73 | return 0; 74 | }else 75 | { 76 | return 2; 77 | } 78 | } 79 | function stateCover2proc(state) 80 | { 81 | if (state=='down') 82 | { 83 | return 0; 84 | } 85 | else if(state=='up') 86 | { 87 | return 100; 88 | }else 89 | { 90 | return 50; 91 | } 92 | } 93 | function aqi2AirQualityMainlandChina(state) 94 | { 95 | if (state <= 50) 96 | { 97 | return 1;//EXCELLENT 98 | } 99 | else if(state <= 100) 100 | { 101 | return 2;//GOOD 102 | } 103 | else if(state <= 200) 104 | { 105 | return 3;//FAIR 106 | } 107 | else if(state <= 300) 108 | { 109 | return 4;//INFERIOR 110 | } 111 | else 112 | { 113 | return 5;//POOR 114 | } 115 | } 116 | function co2AirQuality(state) 117 | { 118 | if (state <= 900) 119 | { 120 | return 1;//EXCELLENT 121 | } 122 | else if(state <= 1150) 123 | { 124 | return 2;//GOOD 125 | } 126 | else if(state <= 1400) 127 | { 128 | return 3;//FAIR 129 | } 130 | else if(state <= 1600) 131 | { 132 | return 4;//INFERIOR 133 | } 134 | else 135 | { 136 | return 5;//POOR 137 | } 138 | } 139 | function co2CarbonDioxideDetected(state) 140 | { 141 | if (state > 1600) 142 | { 143 | return 1;//CO2_LEVELS_ABNORMAL 144 | } 145 | else 146 | { 147 | return 0;//CO2_LEVELS_NORMAL 148 | } 149 | } 150 | function targetTemperature2correct(state) 151 | { 152 | if (state < 10) 153 | { 154 | return 10; 155 | } 156 | else if(state>38) 157 | { 158 | return 38; 159 | }else 160 | { 161 | return state; 162 | } 163 | } 164 | function targeTemperature2thermostatMode(state) 165 | { 166 | if (state == 25) 167 | { 168 | return 1; 169 | } 170 | else if(state==20) 171 | { 172 | return 2; 173 | }else 174 | { 175 | return 3; 176 | } 177 | } 178 | function targeTemperature2currentThermostatMode(state) 179 | { 180 | if (state == 25) 181 | { 182 | return 1; 183 | } 184 | else if(state==20) 185 | { 186 | return 2; 187 | }else 188 | { 189 | return 0; 190 | } 191 | } 192 | 193 | 194 | function thermostatMode2targetHeatingCoolingState(value) 195 | { 196 | if (value == 'heat') 197 | { 198 | return 1;//heat 199 | } 200 | else if (value == 'cool') 201 | { 202 | return 2;//cool 203 | } 204 | else if (value == 'auto') 205 | { 206 | return 3;//auto 207 | } 208 | else 209 | { 210 | return 0; 211 | } 212 | } 213 | 214 | function thermostatMode2currentHeatingCoolingState(value) 215 | { 216 | if (value == 'heat') 217 | { 218 | return 1;//heat 219 | } 220 | else if (value == 'cool') 221 | { 222 | return 2;//cool 223 | } 224 | else if (value == 'auto') 225 | { 226 | return 2;//cool 227 | } 228 | else 229 | { 230 | return 0; 231 | } 232 | } 233 | 234 | function eurotronicMode2targetHeatingCoolingState(value) 235 | { 236 | if (value == 'MANUFACTURER SPECIFC') 237 | { 238 | return 1;//heat 239 | } 240 | else if (value == 'Energy Save Heat') 241 | { 242 | return 2;//cool 243 | } 244 | else if (value == 'Heat') 245 | { 246 | return 3;//auto 247 | } 248 | else 249 | { 250 | return 0; 251 | } 252 | } 253 | 254 | function eurotronicMode2currentHeatingCoolingState(value) 255 | { 256 | if (value == 'MANUFACTURER SPECIFC') 257 | { 258 | return 1;//heat 259 | } 260 | else if (value == 'Energy Save Heat') 261 | { 262 | return 2;//cool 263 | } 264 | else if (value == 'Heat') 265 | { 266 | return 2;//cool 267 | } 268 | else 269 | { 270 | return 0; 271 | } 272 | } 273 | 274 | function vacuumcleaner2state(state) 275 | { 276 | if (state == 'cleaning') 277 | { 278 | return true;//heat 279 | } 280 | else 281 | { 282 | return false; 283 | } 284 | } 285 | 286 | 287 | function alarmBattery(state) 288 | { 289 | if (state <= 10 ) 290 | { 291 | return 1; 292 | } 293 | else 294 | { 295 | return 0; 296 | } 297 | } 298 | 299 | function homealarm_state2SecuritySystemTargetState(state) 300 | { 301 | if (state == 'armed') 302 | { 303 | return 1; 304 | } 305 | else if (state == 'disarmed') 306 | { 307 | return 3; 308 | } 309 | else if (state == 'partially_armed') 310 | { 311 | return 0; 312 | } 313 | else 314 | { 315 | return 2; 316 | } 317 | } 318 | 319 | function setOffForButton(onButton) 320 | { 321 | try { 322 | onButton.setValue(false); 323 | } catch(error) 324 | { 325 | 326 | } 327 | } 328 | 329 | async function updateStatus(device,variable,status,callback) 330 | { 331 | if (device.available) 332 | { 333 | if (device.state[status] != variable && variable != null) 334 | { 335 | console.log(device.name + ': set ' + status + " = " + device.state[status] + " => " + variable, 'info'); 336 | 337 | if (('zw_wakeup_interval' in device.settings) && (device.settings.zw_wakeup_interval > 15)) 338 | { 339 | console.log(device.name + ': zw_wakeup_interval = ' + device.settings.zw_wakeup_interval, 'info'); 340 | try 341 | { 342 | device.setCapabilityValue(status, variable); 343 | } catch(error) 344 | { 345 | console.log(device.name + ': ' + error, 'error'); 346 | } 347 | callback(HAS.statusCodes.Ok); 348 | }else 349 | { 350 | try 351 | { 352 | await device.setCapabilityValue(status, variable); 353 | callback(HAS.statusCodes.Ok); 354 | } catch(error) 355 | { 356 | console.log(device.name + ': ' + error, 'error'); 357 | //use another status code based on error and then callback it 358 | if (error == 'Error: senddata_timeout') 359 | { 360 | callback(HAS.statusCodes.timedout); 361 | }else if (error == 'Error: TRANSMIT_COMPLETE_NO_ACK') 362 | { 363 | callback(HAS.statusCodes.Ok); 364 | }else 365 | { 366 | callback(HAS.statusCodes.busy); 367 | } 368 | } 369 | } 370 | }else 371 | { 372 | callback(HAS.statusCodes.OK); 373 | } 374 | 375 | }else 376 | { 377 | console.log(device.name + ': not available ', 'error'); 378 | callback(HAS.statusCodes.busy); 379 | } 380 | } 381 | 382 | function updateStatusOnHKNoNull(variable, deviceHK, status, device) 383 | { 384 | if (deviceHK.getValue() != variable) 385 | { 386 | console.log('Update: '+ device.name + ' ' + status + ': ' + deviceHK.value + ' new: ' + variable, "info"); 387 | 388 | try { 389 | deviceHK.setValue(variable); 390 | } catch(error) { 391 | console.log(device.name + ': set value ' + error, 'error'); 392 | } 393 | 394 | } 395 | } 396 | 397 | 398 | function createDevice(device, id) 399 | { 400 | //console.log('capabilities: '+ JSON.stringify(device.capabilities)); 401 | //console.log('______________'); 402 | console.log(device.name + ' full info: '+ JSON.stringify(device)); 403 | 404 | let serviceNum = 1; 405 | let characteristicNum = 1; 406 | 407 | 408 | 409 | // New device 410 | let newDevice = new HAS.Accessory(id); 411 | 412 | // What happens when a user presses identify in the Home app (Idea: add speech output or blinking light?) 413 | 414 | let lightIdentify = HAS.predefined.Identify(characteristicNum++, undefined, function(value, callback) { 415 | 416 | callback(HAS.statusCodes.OK); 417 | }); 418 | // Set light details 419 | let lightManufacturer = HAS.predefined.Manufacturer(characteristicNum++, device.driver.owner_name); 420 | let lightModel = HAS.predefined.Model(characteristicNum++, device.driver.id); 421 | let lightName = HAS.predefined.Name(characteristicNum++, device.name); 422 | let lightSerialNumber = HAS.predefined.SerialNumber(characteristicNum++, device.id); 423 | let lightFirmwareVersion = HAS.predefined.FirmwareRevision(characteristicNum++, '1.0.0'); 424 | 425 | // Add services to the light 426 | newDevice.addServices(HAS.predefined.AccessoryInformation(serviceNum++, [lightIdentify, lightManufacturer, lightModel, lightName, lightSerialNumber, lightFirmwareVersion])); 427 | 428 | 429 | 430 | // Create empty capabilities array 431 | let capabilities = []; 432 | 433 | 434 | 435 | // If device has onoff capability 436 | if ('onoff' in device.capabilities) 437 | { 438 | 439 | 440 | if (device.class == 'fan') 441 | { 442 | let variable = state2num(device.state.onoff || false); 443 | 444 | var on = HAS.predefined.Active(characteristicNum++, variable, (value, callback) => 445 | { 446 | updateStatus (device, value == 1, "onoff", callback); 447 | }); 448 | } 449 | else 450 | { 451 | let variable = device.state.onoff || false; 452 | 453 | if ('dim' in device.capabilities) 454 | { 455 | var on = HAS.predefined.On(characteristicNum++, variable, (value, callback) => 456 | { 457 | setTimeout(updateStatus, 1, device, value, "onoff", callback); 458 | }); 459 | }else 460 | { 461 | var on = HAS.predefined.On(characteristicNum++, variable, (value, callback) => 462 | { 463 | updateStatus (device, value, "onoff", callback); 464 | }); 465 | } 466 | } 467 | // Push to array 468 | capabilities.push(on);// add services to light 469 | 470 | // If device has dim capability 471 | if ('dim' in device.capabilities) 472 | { 473 | var dimVariable = HAS.predefined.Brightness(characteristicNum++, Math.floor(device.state.dim * 100 || 100), (value, callback) => 474 | { 475 | updateStatus (device, value / 100, "dim", callback); 476 | }); 477 | capabilities.push(dimVariable); 478 | } 479 | // If device has hue capability 480 | if ('light_hue' in device.capabilities) 481 | { 482 | var hue = HAS.predefined.Hue(characteristicNum++, Math.floor(device.state.light_hue * 360 || 360), (value, callback) => 483 | { 484 | updateStatus (device, Math.ceil((value / 360)*100)/100, "light_hue", callback); 485 | }); 486 | capabilities.push(hue); 487 | } 488 | 489 | // If device has sat capability 490 | if ('light_saturation' in device.capabilities) 491 | { 492 | var sat = HAS.predefined.Saturation(characteristicNum++, Math.floor(device.state.light_saturation * 100 || 100), (value, callback) => 493 | { 494 | updateStatus (device, value / 100, "light_saturation", callback); 495 | }); 496 | capabilities.push(sat); 497 | } else if ('light_hue' in device.capabilities) 498 | { 499 | var sat = HAS.predefined.Saturation(characteristicNum++, 100, (value, callback) => 500 | { 501 | callback(HAS.statusCodes.OK); 502 | }); 503 | capabilities.push(sat); 504 | } 505 | 506 | if ('light_temperature' in device.capabilities) 507 | { 508 | var temp = HAS.predefined.ColorTemperature(characteristicNum++, Math.floor(140 + device.state.light_temperature * 360 || 140), (value, callback) => 509 | { 510 | updateStatus (device, Math.ceil(((value - 140) / 360)*100)/100, "light_temperature", callback); 511 | }); 512 | capabilities.push(temp); 513 | } 514 | 515 | if (device.class == 'light') 516 | { 517 | newDevice.addServices(HAS.predefined.Lightbulb(serviceNum++, capabilities)); 518 | capabilities = []; 519 | }else if (device.class == 'fan') 520 | { 521 | newDevice.addServices(HAS.predefined.Fanv2(serviceNum++, capabilities)); 522 | capabilities = []; 523 | } 524 | else if(device.class == 'heater') 525 | { 526 | newDevice.addServices(HAS.predefined.Switch(serviceNum++, capabilities)); 527 | capabilities = []; 528 | } 529 | else 530 | { 531 | var inuse = HAS.predefined.OutletInUse(characteristicNum++, true, (value, callback) => 532 | { 533 | callback(HAS.statusCodes.OK); 534 | }); 535 | capabilities.push(inuse); 536 | 537 | newDevice.addServices(HAS.predefined.Outlet(serviceNum++, capabilities)); 538 | capabilities = []; 539 | } 540 | 541 | } 542 | 543 | 544 | if ('locked' in device.capabilities) 545 | { 546 | 547 | var LockCurrentState = HAS.predefined.LockCurrentState(characteristicNum++, state2num(device.state.locked || false)); 548 | capabilities.push(LockCurrentState); 549 | 550 | var LockTargetState = HAS.predefined.LockTargetState(characteristicNum++, state2num(device.state.locked || false), (value, callback) => 551 | { 552 | updateStatus (device, value == 1, "locked", callback); 553 | }); 554 | capabilities.push(LockTargetState); 555 | 556 | newDevice.addServices(HAS.predefined.LockMechanism(serviceNum++, capabilities)); 557 | capabilities = []; 558 | } 559 | 560 | // If device has onoff capability 561 | if ('alarm_motion' in device.capabilities) 562 | { 563 | var motion = HAS.predefined.MotionDetected(characteristicNum++, device.state.alarm_motion || false); 564 | newDevice.addServices(HAS.predefined.MotionSensor(serviceNum++, [motion])); 565 | } 566 | 567 | if ('measure_humidity' in device.capabilities) 568 | { 569 | var currentRelativeHumidity = HAS.predefined.CurrentRelativeHumidity(characteristicNum++, Math.floor(device.state.measure_humidity || 0)); 570 | newDevice.addServices(HAS.predefined.HumiditySensor(serviceNum++, [currentRelativeHumidity])); 571 | } 572 | 573 | if ('measure_luminance' in device.capabilities) 574 | { 575 | var lightSensor = HAS.predefined.CurrentAmbientLightLevel(characteristicNum++, device.state.measure_luminance || 0.1); 576 | newDevice.addServices(HAS.predefined.LightSensor(serviceNum++, [lightSensor])); 577 | } 578 | 579 | if ('alarm_co2' in device.capabilities) 580 | { 581 | var carbonDioxideDetected = HAS.predefined.CarbonDioxideDetected(characteristicNum++, state2num(device.state.alarm_co2 || false) , (value, callback) => 582 | { 583 | callback(HAS.statusCodes.OK); 584 | }); 585 | capabilities.push(carbonDioxideDetected); 586 | } 587 | 588 | if ('measure_co2' in device.capabilities) 589 | { 590 | var airQuality = HAS.predefined.AirQuality(characteristicNum++, co2AirQuality(device.state.measure_co2 || 0)); 591 | newDevice.addServices(HAS.predefined.AirQualitySensor(serviceNum++, [airQuality])); 592 | 593 | var carbonDioxideLevel = HAS.predefined.CarbonDioxideLevel(characteristicNum++, device.state.measure_co2 || 0, (value, callback) => 594 | { 595 | callback(HAS.statusCodes.OK); 596 | }); 597 | capabilities.push(carbonDioxideLevel); 598 | 599 | var сarbonDioxidePeakLevel = HAS.predefined.CarbonDioxidePeakLevel(characteristicNum++, 1600, (value, callback) => 600 | { 601 | callback(HAS.statusCodes.OK); 602 | }); 603 | capabilities.push(сarbonDioxidePeakLevel); 604 | 605 | if ('alarm_co2' in device.capabilities == false) 606 | { 607 | var carbonDioxideDetected = HAS.predefined.CarbonDioxideDetected(characteristicNum++, co2CarbonDioxideDetected(device.state.measure_co2 || 0) , (value, callback) => 608 | { 609 | callback(HAS.statusCodes.OK); 610 | }); 611 | capabilities.push(carbonDioxideDetected); 612 | } 613 | 614 | newDevice.addServices(HAS.predefined.CarbonDioxideSensor(serviceNum++, capabilities)); 615 | capabilities = []; 616 | } 617 | 618 | 619 | 620 | if ('alarm_battery' in device.capabilities) 621 | { 622 | var statusLowBattery = HAS.predefined.StatusLowBattery(characteristicNum++, state2num(device.state.alarm_battery || false), (value, callback) => 623 | { 624 | callback(HAS.statusCodes.OK); 625 | }); 626 | capabilities.push(statusLowBattery); 627 | } 628 | 629 | 630 | if ('measure_battery' in device.capabilities) 631 | { 632 | var batteryLevel = HAS.predefined.BatteryLevel(characteristicNum++, device.state.measure_battery || 100, (value, callback) => 633 | { 634 | callback(HAS.statusCodes.OK); 635 | }); 636 | capabilities.push(batteryLevel); 637 | 638 | var chargingState = HAS.predefined.ChargingState(characteristicNum++, 2, (value, callback) => 639 | { 640 | callback(HAS.statusCodes.OK); 641 | }); 642 | capabilities.push(chargingState); 643 | 644 | if ('alarm_battery' in device.capabilities == false) 645 | { 646 | var statusLowBattery = HAS.predefined.StatusLowBattery(characteristicNum++, alarmBattery(device.state.measure_battery || 100), (value, callback) => 647 | { 648 | callback(HAS.statusCodes.OK); 649 | }); 650 | capabilities.push(statusLowBattery); 651 | } 652 | 653 | newDevice.addServices(HAS.predefined.BatteryService(serviceNum++, capabilities)); 654 | capabilities = []; 655 | 656 | } 657 | 658 | 659 | if ('button' in device.capabilities) 660 | { 661 | var onButton = HAS.predefined.On(characteristicNum++, false, (value, callback) => 662 | { 663 | try 664 | { 665 | device.setCapabilityValue("button", value); 666 | } catch(error) 667 | { 668 | console.log(device.name + ': ' + error, 'error'); 669 | } 670 | callback(HAS.statusCodes.OK); 671 | setTimeout(setOffForButton, 100, onButton); 672 | }); 673 | newDevice.addServices(HAS.predefined.Switch(serviceNum++, [onButton])); 674 | } 675 | 676 | if ('speaker_playing' in device.capabilities) 677 | { 678 | var onSpeakerPlaying = HAS.predefined.On(characteristicNum++, device.state.speaker_playing || false, (value, callback) => 679 | { 680 | updateStatus (device, value, "speaker_playing", callback); 681 | }); 682 | capabilities.push(onSpeakerPlaying); 683 | 684 | var newName = HAS.predefined.Name(characteristicNum++, device.name + " (Play/Pause)"); 685 | capabilities.push(newName); 686 | 687 | newDevice.addServices(HAS.predefined.Switch(serviceNum++, capabilities)); 688 | capabilities = []; 689 | } 690 | 691 | if ('speaker_next' in device.capabilities) 692 | { 693 | var onSpeakerNext = HAS.predefined.On(characteristicNum++, false, (value, callback) => 694 | { 695 | try 696 | { 697 | device.setCapabilityValue("speaker_next", value); 698 | } catch(error) 699 | { 700 | console.log(device.name + ': ' + error, 'error'); 701 | } 702 | callback(HAS.statusCodes.OK); 703 | setTimeout(setOffForButton, 100, onSpeakerNext); 704 | }); 705 | capabilities.push(onSpeakerNext); 706 | 707 | var newName = HAS.predefined.Name(characteristicNum++, device.name + " (next)"); 708 | capabilities.push(newName); 709 | 710 | newDevice.addServices(HAS.predefined.Switch(serviceNum++, capabilities)); 711 | capabilities = []; 712 | } 713 | 714 | if ('speaker_prev' in device.capabilities) 715 | { 716 | var onSpeakerPrev = HAS.predefined.On(characteristicNum++, false, (value, callback) => 717 | { 718 | try 719 | { 720 | device.setCapabilityValue("speaker_prev", value); 721 | } catch(error) 722 | { 723 | console.log(device.name + ': ' + error, 'error'); 724 | } 725 | callback(HAS.statusCodes.OK); 726 | setTimeout(setOffForButton, 100, onSpeakerPrev); 727 | }); 728 | capabilities.push(onSpeakerPrev); 729 | 730 | var newName = HAS.predefined.Name(characteristicNum++, device.name + " (Prev)"); 731 | capabilities.push(newName); 732 | 733 | newDevice.addServices(HAS.predefined.Switch(serviceNum++, capabilities)); 734 | capabilities = []; 735 | } 736 | 737 | 738 | 739 | if ('measure_temperature' in device.capabilities) 740 | { 741 | var temperature = HAS.predefined.CurrentTemperature(characteristicNum++); 742 | temperature.minValue = - temperature.maxValue; 743 | temperature.stepValue = 0.01; 744 | updateStatusOnHKNoNull(device.state.measure_temperature || 0, temperature, 'temperature', device); 745 | 746 | if (device.class == 'thermostat') 747 | { 748 | capabilities.push(temperature); 749 | } 750 | else 751 | { 752 | newDevice.addServices(HAS.predefined.TemperatureSensor(serviceNum++, [temperature])); 753 | } 754 | 755 | } 756 | 757 | if ('alarm_water' in device.capabilities) 758 | { 759 | var leakDetected = HAS.predefined.LeakDetected(characteristicNum++, state2num(device.state.alarm_water || false)); 760 | newDevice.addServices(HAS.predefined.LeakSensor(serviceNum++, [leakDetected])); 761 | } 762 | 763 | if ('alarm_smoke' in device.capabilities) 764 | { 765 | var smokeDetected = HAS.predefined.SmokeDetected(characteristicNum++, state2num(device.state.alarm_smoke || false)); 766 | newDevice.addServices(HAS.predefined.SmokeSensor(serviceNum++, [smokeDetected])); 767 | } 768 | 769 | if ('alarm_contact' in device.capabilities) 770 | { 771 | var contactSensorState = HAS.predefined.ContactSensorState(characteristicNum++, state2num(device.state.alarm_contact || false)); 772 | newDevice.addServices(HAS.predefined.ContactSensor(serviceNum++, [contactSensorState])); 773 | } 774 | 775 | if ('measure_aqi' in device.capabilities) 776 | { 777 | var airQuality = HAS.predefined.AirQuality(characteristicNum++, aqi2AirQualityMainlandChina(device.state.measure_aqi || 0)); 778 | newDevice.addServices(HAS.predefined.AirQualitySensor(serviceNum++, [airQuality])); 779 | } 780 | 781 | 782 | if ('windowcoverings_state' in device.capabilities && 'dim' in device.capabilities == false) 783 | { 784 | var currentPosition = HAS.predefined.CurrentPosition(characteristicNum++, stateCover2proc(device.state.windowcoverings_state || 'idle'), (value, callback) => 785 | { 786 | callback(HAS.statusCodes.OK); 787 | }); 788 | capabilities.push(currentPosition); 789 | 790 | var dimVariable = HAS.predefined.TargetPosition(characteristicNum++, stateCover2proc(device.state.windowcoverings_state || 'idle'), (value, callback) => 791 | { 792 | let variable = 'idle'; 793 | if (value == 100) 794 | { 795 | variable = 'up'; 796 | } 797 | else if (value == 0) 798 | { 799 | variable = 'down'; 800 | } 801 | updateStatus (device, variable, "windowcoverings_state", callback); 802 | }); 803 | capabilities.push(dimVariable); 804 | 805 | var positionState = HAS.predefined.PositionState(characteristicNum++, stateCover2num(device.state.windowcoverings_state || 'idle'), (value, callback) => 806 | { 807 | callback(HAS.statusCodes.OK); 808 | }); 809 | capabilities.push(positionState); 810 | 811 | newDevice.addServices(HAS.predefined.WindowCovering(serviceNum++, capabilities)); 812 | capabilities = []; 813 | }else if (device.class == 'windowcoverings' && 'dim' in device.capabilities) 814 | { 815 | var currentPosition = HAS.predefined.CurrentPosition(characteristicNum++, device.state.dim*100 || 100, (value, callback) => 816 | { 817 | callback(HAS.statusCodes.OK); 818 | }); 819 | capabilities.push(currentPosition); 820 | 821 | var dimVariable = HAS.predefined.TargetPosition(characteristicNum++, device.state.dim*100 || 100, (value, callback) => 822 | { 823 | updateStatus (device, value/100, "dim", callback); 824 | }); 825 | capabilities.push(dimVariable); 826 | 827 | if ('windowcoverings_state' in device.capabilities) 828 | { 829 | var positionState = HAS.predefined.PositionState(characteristicNum++, stateCover2num(device.state.windowcoverings_state || 'idle'), (value, callback) => 830 | { 831 | callback(HAS.statusCodes.OK); 832 | }); 833 | capabilities.push(positionState); 834 | } 835 | else 836 | { 837 | var positionState = HAS.predefined.PositionState(characteristicNum++, 2, (value, callback) => 838 | { 839 | callback(HAS.statusCodes.OK); 840 | }); 841 | capabilities.push(positionState); 842 | 843 | } 844 | 845 | newDevice.addServices(HAS.predefined.WindowCovering(serviceNum++, capabilities)); 846 | capabilities = []; 847 | 848 | } 849 | 850 | if ('thermostat_mode' in device.capabilities) 851 | { 852 | 853 | var targetHeatingCoolingState = HAS.predefined.TargetHeatingCoolingState(characteristicNum++,thermostatMode2targetHeatingCoolingState(device.state.thermostat_mode || 'cool') , (value, callback) => 854 | { 855 | let variable = 'off'; 856 | if (value == 1) 857 | { 858 | variable = 'heat'; 859 | } 860 | else if (value == 2) 861 | { 862 | variable = 'cool'; 863 | } 864 | else if (value == 3) 865 | { 866 | variable = 'auto'; 867 | } 868 | 869 | updateStatus (device, variable, "thermostat_mode", callback); 870 | }); 871 | capabilities.push(targetHeatingCoolingState); 872 | 873 | var currentHeatingCoolingState = HAS.predefined.CurrentHeatingCoolingState(characteristicNum++, thermostatMode2currentHeatingCoolingState(device.state.thermostat_mode || 'cool'), (value, callback) => 874 | { 875 | callback(HAS.statusCodes.OK); 876 | }); 877 | capabilities.push(currentHeatingCoolingState); 878 | 879 | } else if ('eurotronic_mode' in device.capabilities) 880 | { 881 | 882 | var targetHeatingCoolingState = HAS.predefined.TargetHeatingCoolingState(characteristicNum++,eurotronicMode2targetHeatingCoolingState(device.state.eurotronic_mode || 'Energy Save Heat') , (value, callback) => 883 | { 884 | let variable = 'Off'//off; 885 | if (value == 1) 886 | { 887 | variable = 'MANUFACTURER SPECIFC'//heat; 888 | } 889 | else if (value == 2) 890 | { 891 | variable = 'Energy Save Heat'//cool; 892 | } 893 | else if (value == 3) 894 | { 895 | variable = 'Heat'//auto; 896 | } 897 | 898 | updateStatus (device, variable, "eurotronic_mode", callback); 899 | }); 900 | capabilities.push(targetHeatingCoolingState); 901 | 902 | var currentHeatingCoolingState = HAS.predefined.CurrentHeatingCoolingState(characteristicNum++, eurotronicMode2currentHeatingCoolingState(device.state.eurotronic_mode || 'Energy Save Heat'), (value, callback) => 903 | { 904 | callback(HAS.statusCodes.OK); 905 | }); 906 | capabilities.push(currentHeatingCoolingState); 907 | 908 | } 909 | 910 | if ('target_temperature' in device.capabilities) 911 | { 912 | 913 | if ('thermostat_mode' in device.capabilities == false && 'eurotronic_mode' in device.capabilities == false) 914 | { 915 | 916 | var targetHeatingCoolingState = HAS.predefined.TargetHeatingCoolingState(characteristicNum++, targeTemperature2thermostatMode(device.state.target_temperature || 20), (value, callback) => 917 | { 918 | let variable = device.state.target_temperature; 919 | 920 | if (value == 1) 921 | { 922 | variable = 25; 923 | } 924 | else if (value == 2) 925 | { 926 | variable = 20; 927 | } 928 | else if (value == 3) 929 | { 930 | variable = 23; 931 | } 932 | 933 | updateStatus (device, variable, "target_temperature", callback); 934 | }); 935 | 936 | capabilities.push(targetHeatingCoolingState); 937 | 938 | var currentHeatingCoolingState = HAS.predefined.CurrentHeatingCoolingState(characteristicNum++, targeTemperature2currentThermostatMode(device.state.target_temperature || 20), (value, callback) => 939 | { 940 | callback(HAS.statusCodes.OK); 941 | }); 942 | capabilities.push(currentHeatingCoolingState); 943 | 944 | } 945 | 946 | var targetTemperature = HAS.predefined.TargetTemperature(characteristicNum++, targetTemperature2correct(device.state.target_temperature || 20), (value, callback) => 947 | { 948 | 949 | let max = 28; 950 | 951 | if ('max' in device.capabilitiesOptions.target_temperature) 952 | { 953 | max = device.capabilitiesOptions.target_temperature.max; 954 | //console.log(device.name + ' max: '+ max); 955 | } 956 | 957 | value = value > max?max:value; 958 | 959 | updateStatus (device, value, "target_temperature", callback); 960 | 961 | if ('measure_temperature' in device.capabilities == false) 962 | { 963 | updateStatusOnHKNoNull(value, temperature, "temperature", device); 964 | } 965 | 966 | }); 967 | 968 | capabilities.push(targetTemperature); 969 | 970 | var temperatureDisplayUnits = HAS.predefined.TemperatureDisplayUnits(characteristicNum++, 0, (value, callback) => 971 | { 972 | callback(HAS.statusCodes.OK); 973 | }); 974 | capabilities.push(temperatureDisplayUnits); 975 | 976 | if ('measure_temperature' in device.capabilities == false) 977 | { 978 | var temperature = HAS.predefined.CurrentTemperature(characteristicNum++, targetTemperature2correct(device.state.target_temperature || 20)); 979 | capabilities.push(temperature); 980 | } 981 | 982 | newDevice.addServices(HAS.predefined.Thermostat(serviceNum++, capabilities)); 983 | capabilities = []; 984 | } 985 | 986 | if ('volume_mute' in device.capabilities) 987 | { 988 | var mute = HAS.predefined.Mute(characteristicNum++, device.state.volume_mute || false, (value, callback) => 989 | { 990 | updateStatus (device, value, "volume_mute", callback); 991 | }); 992 | capabilities.push(mute); 993 | } 994 | 995 | if ('volume_set' in device.capabilities) 996 | { 997 | var volume = HAS.predefined.Volume(characteristicNum++, device.state.volume_set * 100 || 10, (value, callback) => 998 | { 999 | updateStatus (device, value / 100, "volume_set", callback); 1000 | }); 1001 | capabilities.push(volume); 1002 | 1003 | if ('volume_mute' in device.capabilities == false) 1004 | { 1005 | var mute = HAS.predefined.Mute(characteristicNum++, device.state.volume_set == 0 || false, (value, callback) => 1006 | { 1007 | updateStatus (device, (value ? 0: 20/100), "volume_set", callback); 1008 | }); 1009 | capabilities.push(mute); 1010 | } 1011 | 1012 | newDevice.addServices(HAS.predefined.Speaker(serviceNum++, capabilities)); 1013 | capabilities = []; 1014 | } 1015 | 1016 | if ('vacuumcleaner_state' in device.capabilities) 1017 | { 1018 | var vacuumcOn = HAS.predefined.On(characteristicNum++, vacuumcleaner2state(device.state.vacuumcleaner_state || 'stopped'), (value, callback) => 1019 | { 1020 | updateStatus (device, (value ? "cleaning": "docked"), "vacuumcleaner_state", callback); 1021 | }); 1022 | capabilities.push(vacuumcOn); 1023 | 1024 | var inuse = HAS.predefined.OutletInUse(characteristicNum++, true, (value, callback) => 1025 | { 1026 | callback(HAS.statusCodes.OK); 1027 | }); 1028 | capabilities.push(inuse); 1029 | 1030 | newDevice.addServices(HAS.predefined.Outlet(serviceNum++, capabilities)); 1031 | capabilities = []; 1032 | } 1033 | 1034 | if ('alarm_generic' in device.capabilities) 1035 | { 1036 | var newName = HAS.predefined.Name(characteristicNum++, device.name + " (Button)"); 1037 | capabilities.push(newName); 1038 | 1039 | var motionDoorbell = HAS.predefined.MotionDetected(characteristicNum++, device.state.alarm_generic || false); 1040 | capabilities.push(motionDoorbell); 1041 | 1042 | newDevice.addServices(HAS.predefined.MotionSensor(serviceNum++, capabilities)); 1043 | capabilities = []; 1044 | } 1045 | 1046 | if ('alarm_tamper' in device.capabilities) 1047 | { 1048 | var occupancyDetected = HAS.predefined.OccupancyDetected(characteristicNum++, state2num(device.state.alarm_tamper || false)); 1049 | capabilities.push(occupancyDetected); 1050 | 1051 | newDevice.addServices(HAS.predefined.OccupancySensor(serviceNum++, capabilities)); 1052 | capabilities = []; 1053 | } 1054 | 1055 | if ('measure_pressure' in device.capabilities) 1056 | { 1057 | var currentAtmosphericPressure = NewTypes.CurrentAtmosphericPressure(characteristicNum++, device.state.measure_pressure || 0); 1058 | capabilities.push(currentAtmosphericPressure); 1059 | 1060 | newDevice.addServices(NewTypes.AtmosphericPressureSensor(serviceNum++, capabilities)); 1061 | capabilities = []; 1062 | } 1063 | 1064 | if ('measure_noise' in device.capabilities) 1065 | { 1066 | var currentNoiseLevel = NewTypes.CurrentNoiseLevel(characteristicNum++, device.state.measure_noise || 0); 1067 | capabilities.push(currentNoiseLevel); 1068 | 1069 | newDevice.addServices(NewTypes.NoiseLevelSensor(serviceNum++, capabilities)); 1070 | capabilities = []; 1071 | } 1072 | 1073 | if ('measure_ultraviolet' in device.capabilities) 1074 | { 1075 | var currentUltraviolet = NewTypes.CurrentUltraviolet(characteristicNum++, device.state.measure_ultraviolet || 0); 1076 | capabilities.push(currentUltraviolet); 1077 | 1078 | newDevice.addServices(NewTypes.UltravioletSensor(serviceNum++, capabilities)); 1079 | capabilities = []; 1080 | } 1081 | 1082 | if ('measure_power' in device.capabilities) 1083 | { 1084 | var watts = NewTypes.Watts(characteristicNum++, device.state.measure_power || 0); 1085 | capabilities.push(watts); 1086 | 1087 | newDevice.addServices(NewTypes.PowerMeter(serviceNum++, capabilities)); 1088 | capabilities = []; 1089 | } 1090 | 1091 | if ('homealarm_state' in device.capabilities) 1092 | { 1093 | var securitySystemCurrentState = HAS.predefined.SecuritySystemCurrentState(characteristicNum++, homealarm_state2SecuritySystemTargetState(device.state.homealarm_state || 'disarmed')); 1094 | capabilities.push(securitySystemCurrentState); 1095 | 1096 | var securitySystemTargetState = HAS.predefined.SecuritySystemTargetState(characteristicNum++, homealarm_state2SecuritySystemTargetState(device.state.homealarm_state || 'disarmed'), (value, callback) => 1097 | { 1098 | let variable = 'disarmed'; 1099 | 1100 | if (value == 0) 1101 | { 1102 | variable = 'partially_armed'; 1103 | } 1104 | else if (value == 1) 1105 | { 1106 | variable = 'armed'; 1107 | } 1108 | else if (value == 2) 1109 | { 1110 | variable = 'partially_armed'; 1111 | } 1112 | else if (value == 3) 1113 | { 1114 | variable = 'disarmed'; 1115 | } 1116 | callback(HAS.statusCodes.OK); 1117 | updateStatus (device, variable, "homealarm_state", callback); 1118 | }); 1119 | capabilities.push(securitySystemTargetState); 1120 | 1121 | newDevice.addServices(HAS.predefined.SecuritySystem(serviceNum++, capabilities)); 1122 | capabilities = []; 1123 | } 1124 | 1125 | 1126 | device.on('$state', state => 1127 | { 1128 | 1129 | console.log('Realtime event from: ' + device.name + '. Value: ' + JSON.stringify(state), "info"); 1130 | 1131 | if (device.available) 1132 | { 1133 | if ('onoff' in device.capabilities) 1134 | { 1135 | let variable = state.onoff; 1136 | 1137 | if (variable != null) 1138 | { 1139 | if (device.class == 'fan') 1140 | { 1141 | variable = state2num(variable); 1142 | } 1143 | 1144 | updateStatusOnHKNoNull(variable, on, "onoff", device); 1145 | } 1146 | } 1147 | 1148 | if ('dim' in device.capabilities) 1149 | { 1150 | let variable = state.dim; 1151 | 1152 | if (variable != null) 1153 | { 1154 | variable = Math.floor(variable * 100); 1155 | 1156 | updateStatusOnHKNoNull(variable, dimVariable, "dim", device); 1157 | 1158 | if(device.class == 'windowcoverings') 1159 | { 1160 | updateStatusOnHKNoNull(variable, currentPosition, "currentPosition", device); 1161 | } 1162 | } 1163 | } 1164 | 1165 | if ('alarm_co2' in device.capabilities) 1166 | { 1167 | let variable = state.alarm_co2; 1168 | 1169 | if (variable != null) 1170 | { 1171 | updateStatusOnHKNoNull(state2num(variable), carbonDioxideDetected, "alarm_co2", device); 1172 | } 1173 | } 1174 | 1175 | if ('measure_co2' in device.capabilities) 1176 | { 1177 | let variable = state.measure_co2; 1178 | 1179 | if (variable != null) 1180 | { 1181 | updateStatusOnHKNoNull(variable, carbonDioxideLevel, "measure_co2", device); 1182 | updateStatusOnHKNoNull(co2AirQuality(variable), airQuality, "airQuality", device); 1183 | 1184 | if ('alarm_co2' in device.capabilities == false) 1185 | { 1186 | updateStatusOnHKNoNull(co2CarbonDioxideDetected(variable), carbonDioxideDetected, "carbonDioxideDetected", device); 1187 | } 1188 | } 1189 | } 1190 | 1191 | if ('volume_mute' in device.capabilities) 1192 | { 1193 | let variable = state.volume_mute; 1194 | 1195 | if (variable != null) 1196 | { 1197 | updateStatusOnHKNoNull(variable, mute, "volume_mute", device); 1198 | } 1199 | } 1200 | 1201 | if ('volume_set' in device.capabilities) 1202 | { 1203 | let variable = state.volume_set; 1204 | 1205 | if (variable != null) 1206 | { 1207 | variable = Math.floor(variable * 100); 1208 | 1209 | updateStatusOnHKNoNull(variable, volume, "volume_set", device); 1210 | if ('volume_mute' in device.capabilities == false) 1211 | { 1212 | updateStatusOnHKNoNull(variable==0, mute, "mute", device); 1213 | } 1214 | } 1215 | } 1216 | 1217 | if ('light_hue' in device.capabilities) 1218 | { 1219 | let variable = state.light_hue; 1220 | 1221 | if (variable != null) 1222 | { 1223 | variable = Math.floor(variable * 360); 1224 | 1225 | updateStatusOnHKNoNull(variable, hue, "light_hue", device); 1226 | } 1227 | }else 1228 | { 1229 | 1230 | if ('light_saturation' in device.capabilities) 1231 | { 1232 | let variable = state.light_saturation; 1233 | 1234 | if (variable != null) 1235 | { 1236 | variable = Math.floor(variable * 100); 1237 | 1238 | updateStatusOnHKNoNull(variable, sat, "light_saturation", device); 1239 | } 1240 | } 1241 | 1242 | if ('light_temperature' in device.capabilities) 1243 | { 1244 | let variable = state.light_temperature; 1245 | 1246 | if (variable != null) 1247 | { 1248 | variable = Math.floor(140 + variable * 360); 1249 | 1250 | updateStatusOnHKNoNull(variable, temp, "light_temperature", device); 1251 | } 1252 | } 1253 | 1254 | } 1255 | 1256 | if ('alarm_motion' in device.capabilities) 1257 | { 1258 | let variable = state.alarm_motion; 1259 | 1260 | if (variable != null) 1261 | { 1262 | updateStatusOnHKNoNull(variable, motion, "alarm_motion", device); 1263 | } 1264 | } 1265 | 1266 | if ('measure_humidity' in device.capabilities) 1267 | { 1268 | let variable = state.measure_humidity; 1269 | 1270 | if (variable != null) 1271 | { 1272 | updateStatusOnHKNoNull(Math.floor(variable), currentRelativeHumidity, "measure_humidity", device); 1273 | } 1274 | } 1275 | 1276 | if ('measure_luminance' in device.capabilities) 1277 | { 1278 | let variable = state.measure_luminance; 1279 | 1280 | if (variable != null) 1281 | { 1282 | if (variable == 0) 1283 | { 1284 | variable = 0.1; 1285 | } 1286 | updateStatusOnHKNoNull(variable, lightSensor, "measure_luminance", device); 1287 | } 1288 | } 1289 | 1290 | 1291 | if ('alarm_battery' in device.capabilities) 1292 | { 1293 | let variable = state.alarm_battery; 1294 | 1295 | if (variable != null) 1296 | { 1297 | variable = state2num(variable); 1298 | updateStatusOnHKNoNull(variable, statusLowBattery, "alarm_battery", device); 1299 | } 1300 | } 1301 | 1302 | if ('measure_battery' in device.capabilities) 1303 | { 1304 | let variable = state.measure_battery; 1305 | 1306 | if (variable != null) 1307 | { 1308 | updateStatusOnHKNoNull(variable, batteryLevel, "measure_battery", device); 1309 | 1310 | if ('alarm_battery' in device.capabilities == false) 1311 | { 1312 | variable = alarmBattery(variable); 1313 | updateStatusOnHKNoNull(variable, statusLowBattery, "statusLowBattery", device); 1314 | } 1315 | } 1316 | } 1317 | 1318 | 1319 | if ('measure_temperature' in device.capabilities) 1320 | { 1321 | let variable = state.measure_temperature; 1322 | 1323 | if (variable != null) 1324 | { 1325 | updateStatusOnHKNoNull(variable, temperature, "measure_temperature", device); 1326 | } 1327 | } 1328 | 1329 | if ('alarm_water' in device.capabilities) 1330 | { 1331 | let variable = state.alarm_water; 1332 | 1333 | if (variable != null) 1334 | { 1335 | variable = state2num(variable); 1336 | 1337 | updateStatusOnHKNoNull(variable, leakDetected, "alarm_water", device); 1338 | } 1339 | } 1340 | 1341 | if ('alarm_smoke' in device.capabilities) 1342 | { 1343 | let variable = state.alarm_smoke; 1344 | 1345 | if (variable != null) 1346 | { 1347 | variable = state2num(variable); 1348 | 1349 | updateStatusOnHKNoNull(variable, smokeDetected, "alarm_smoke", device); 1350 | } 1351 | } 1352 | 1353 | if ('measure_aqi' in device.capabilities) 1354 | { 1355 | let variable = state.measure_aqi; 1356 | 1357 | if (variable != null) 1358 | { 1359 | variable = aqi2AirQualityMainlandChina(variable); 1360 | 1361 | updateStatusOnHKNoNull(variable, airQuality, "measure_aqi", device); 1362 | } 1363 | } 1364 | 1365 | if ('alarm_contact' in device.capabilities) 1366 | { 1367 | let variable = state.alarm_contact; 1368 | 1369 | if (variable != null) 1370 | { 1371 | variable = state2num(variable); 1372 | 1373 | updateStatusOnHKNoNull(variable, contactSensorState, "alarm_contact", device); 1374 | } 1375 | } 1376 | 1377 | if ('locked' in device.capabilities) 1378 | { 1379 | let variable = state.locked; 1380 | 1381 | if (variable != null) 1382 | { 1383 | variable = state2num(variable); 1384 | 1385 | updateStatusOnHKNoNull(variable, LockCurrentState, "locked", device); 1386 | } 1387 | } 1388 | 1389 | if ('speaker_playing' in device.capabilities) 1390 | { 1391 | let variable = device.state.speaker_playing; 1392 | 1393 | if (variable != null) 1394 | { 1395 | updateStatusOnHKNoNull(variable, onSpeakerPlaying, "onSpeakerPlaying", device); 1396 | } 1397 | else 1398 | { 1399 | updateStatusOnHKNoNull(false, onSpeakerPlaying, "onSpeakerPlaying", device); 1400 | } 1401 | } 1402 | 1403 | if ('windowcoverings_state' in device.capabilities) 1404 | { 1405 | let variable = device.state.windowcoverings_state; 1406 | 1407 | if (variable != null) 1408 | { 1409 | variable = stateCover2proc(variable); 1410 | 1411 | updateStatusOnHKNoNull(variable, currentPosition, "currentPosition", device); 1412 | updateStatusOnHKNoNull(variable, dimVariable, "dimVariable", device); 1413 | //updateStatusOnHKNoNull(stateCover2num(device.state.windowcoverings_state), positionState, "positionState", device); 1414 | } 1415 | } 1416 | 1417 | 1418 | if ('thermostat_mode' in device.capabilities) 1419 | { 1420 | let variable = device.state.thermostat_mode; 1421 | 1422 | if (variable != null) 1423 | { 1424 | variable = thermostatMode2targetHeatingCoolingState(variable); 1425 | 1426 | updateStatusOnHKNoNull(variable, targetHeatingCoolingState, "thermostat_mode", device); 1427 | updateStatusOnHKNoNull(thermostatMode2currentHeatingCoolingState(device.state.thermostat_mode), currentHeatingCoolingState, "currentHeatingCoolingState", device); 1428 | } 1429 | } 1430 | 1431 | if ('eurotronic_mode' in device.capabilities) 1432 | { 1433 | let variable = device.state.eurotronic_mode; 1434 | 1435 | if (variable != null) 1436 | { 1437 | variable = eurotronicMode2targetHeatingCoolingState(variable); 1438 | 1439 | updateStatusOnHKNoNull(variable, targetHeatingCoolingState, "eurotronic_mode", device); 1440 | updateStatusOnHKNoNull(eurotronicMode2currentHeatingCoolingState(device.state.eurotronic_mode), currentHeatingCoolingState, "currentHeatingCoolingState", device); 1441 | } 1442 | } 1443 | 1444 | if ('target_temperature' in device.capabilities) 1445 | { 1446 | let variable = device.state.target_temperature; 1447 | 1448 | if (variable != null) 1449 | { 1450 | variable = targetTemperature2correct(variable); 1451 | 1452 | updateStatusOnHKNoNull(variable, targetTemperature, "target_temperature", device); 1453 | 1454 | if ('measure_temperature' in device.capabilities == false) 1455 | { 1456 | updateStatusOnHKNoNull(variable, temperature, "temperature", device); 1457 | } 1458 | 1459 | if ('thermostat_mode' in device.capabilities == false && 'eurotronic_mode' in device.capabilities == false) 1460 | { 1461 | updateStatusOnHKNoNull(targeTemperature2currentThermostatMode(variable), currentHeatingCoolingState, "currentHeatingCoolingState", device); 1462 | updateStatusOnHKNoNull(targeTemperature2thermostatMode(variable), targetHeatingCoolingState, "targetHeatingCoolingState", device); 1463 | } 1464 | } 1465 | } 1466 | 1467 | if ('vacuumcleaner_state' in device.capabilities) 1468 | { 1469 | let variable = state.vacuumcleaner_state; 1470 | 1471 | if (variable != null) 1472 | { 1473 | variable = vacuumcleaner2state(variable); 1474 | 1475 | updateStatusOnHKNoNull(variable, vacuumcOn, "vacuumcleaner_state", device); 1476 | } 1477 | } 1478 | 1479 | if ('alarm_generic' in device.capabilities) 1480 | { 1481 | let variable = state.alarm_generic; 1482 | 1483 | if (variable != null) 1484 | { 1485 | updateStatusOnHKNoNull(variable, motionDoorbell, "alarm_generic", device); 1486 | } 1487 | } 1488 | 1489 | if ('alarm_tamper' in device.capabilities) 1490 | { 1491 | let variable = state.alarm_tamper; 1492 | 1493 | if (variable != null) 1494 | { 1495 | updateStatusOnHKNoNull(state2num(variable), occupancyDetected, "alarm_tamper", device); 1496 | } 1497 | } 1498 | 1499 | if ('measure_pressure' in device.capabilities) 1500 | { 1501 | let variable = state.measure_pressure; 1502 | 1503 | if (variable != null) 1504 | { 1505 | updateStatusOnHKNoNull(variable, currentAtmosphericPressure, "measure_pressure", device); 1506 | } 1507 | } 1508 | 1509 | if ('measure_noise' in device.capabilities) 1510 | { 1511 | let variable = state.measure_noise; 1512 | 1513 | if (variable != null) 1514 | { 1515 | updateStatusOnHKNoNull(variable, currentNoiseLevel, "measure_noise", device); 1516 | } 1517 | } 1518 | 1519 | if ('measure_ultraviolet' in device.capabilities) 1520 | { 1521 | let variable = state.measure_ultraviolet; 1522 | 1523 | if (variable != null) 1524 | { 1525 | updateStatusOnHKNoNull(variable, currentUltraviolet, "measure_ultraviolet", device); 1526 | } 1527 | } 1528 | 1529 | if ('measure_power' in device.capabilities) 1530 | { 1531 | let variable = state.measure_power; 1532 | 1533 | if (variable != null) 1534 | { 1535 | updateStatusOnHKNoNull(variable, watts, "measure_power", device); 1536 | } 1537 | } 1538 | 1539 | if ('homealarm_state' in device.capabilities) 1540 | { 1541 | let variable = state.homealarm_state; 1542 | 1543 | if (variable != null) 1544 | { 1545 | updateStatusOnHKNoNull(variable, securitySystemCurrentState, "homealarm_state", device); 1546 | updateStatusOnHKNoNull(variable, securitySystemTargetState, "homealarm_state", device); 1547 | } 1548 | } 1549 | 1550 | }else 1551 | { 1552 | console.log(device.name + ': not available ', 'error'); 1553 | } 1554 | }); 1555 | 1556 | return newDevice; 1557 | 1558 | } 1559 | 1560 | 1561 | module.exports = { 1562 | configServer: configServer, 1563 | createDevice: createDevice 1564 | } 1565 | --------------------------------------------------------------------------------