├── 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 |
--------------------------------------------------------------------------------
/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 | 
5 |
6 | 
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 |
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 | | {{log.time}} |
45 |
46 | Info
47 | Success
48 | Error
49 | {{log.string}}
50 | |
51 |
52 |
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 |
--------------------------------------------------------------------------------