Skip to content

Commit

Permalink
Merge pull request #66 from Grizzelbee/Dev_0.7.5
Browse files Browse the repository at this point in the history
Dev 0.7.5
  • Loading branch information
Grizzelbee authored Feb 12, 2021
2 parents d4d58a1 + b2235e1 commit bd2ca16
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 44 deletions.
42 changes: 27 additions & 15 deletions dyson-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ module.exports.checkAdapterConfig = async function (adapter) {
adapter.log.error(`Invalid configuration provided: password is missing. Please enter your password.`);
}
if (!config.country || config.country === '') {
adapter.log.error(`Invalid configuration provided: Country is missing. Please select your Country.`);
adapter.log.error(`Invalid configuration provided: Country is missing. Please select your country.`);
}
if (!config.temperatureUnit || config.temperatureUnit === '') {
adapter.log.error(`Invalid configuration provided: Temperature unit is missing. Please select a Temperature unit.`);
adapter.log.error(`Invalid configuration provided: Temperature unit is missing. Please select a temperature unit.`);
}
if (!config.pollInterval || config.pollInterval === '' || config.pollInterval < 1) {
adapter.log.error(`Invalid configuration provided: Poll interval is not valid. Please enter a valid poll interval (> 0s).`);
Expand Down Expand Up @@ -104,7 +104,7 @@ module.exports.decryptMqttPasswd = function(LocalCredentials) {
* Queries the devices stored in a given dyson online account
*
* @param myAccount {Object} JSON Object containing your dyson account details
* @param adapterLog {Object} link to the adapters log-output to get proper log entries
* @param adapter {Object} link to the adapter
* @returns {ANY}
* resolves with a List of dyson devices connected to the given account
* rejects with an error message
Expand Down Expand Up @@ -150,17 +150,29 @@ module.exports.getDevices = async function(myAccount, adapter) {
* rejects on any http error.
*/
module.exports.dysonAPILogIn = async function(adapter) {
adapter.log.debug('Signing in into Dyson API...');
adapter.log.info('Signing in into dyson cloud API ...');
const headers = {
'User-Agent': 'DysonLink/29019 CFNetwork/1188 Darwin/20.0.0',
'Content-Type': 'application/json'
};
const payload = {
'Email': adapter.config.email,
'Password': adapter.config.Password
};

const response = await axios.get(apiUri + `/v1/userregistration/userstatus?country=${adapter.config.country}&email=${adapter.config.email}`,
{ httpsAgent,
headers: headers,
json: true
});
adapter.log.info(`Result from API-Status request Account is: ${response.data.accountStatus}`);

// Sends the login request to the API
return await axios.post(apiUri + '/v1/userregistration/authenticate?country=' + adapter.config.country,
{
Email: adapter.config.email,
Password: adapter.config.Password
},
payload,
{ httpsAgent,
headers: { 'User-Agent': 'android client' },
json: true,
rejectUnauthorized: false
headers: headers,
json : true
});
};

Expand All @@ -187,7 +199,7 @@ module.exports.dysonAPILogIn = async function(adapter) {
* Function getMqttCredentials
*
*
* @param adapterLog {Object} link to the adapters log-output to get proper log entries
* @param adapter {Object} link to the adapters
* @returns Promise {string} resolves with the MQTT Basic-Auth of the device, rejects with the error which occurred.
*/
module.exports.getMqttCredentials = function(adapter) {
Expand All @@ -201,13 +213,13 @@ module.exports.getMqttCredentials = function(adapter) {
resolve( 'Basic ' + Buffer.from(response.data.Account + ':' + response.data.Password).toString('base64'));
})
.catch((error) => {
adapter.log.error('Error during dyson API login:' + error + ', Callstack: ' + error.stack);
adapter.log.error('Error during dyson cloud API login:' + error + ', Callstack: ' + error.stack);
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
switch (error.response.status) {
case 401 : // unauthorized
adapter.log.error('Error: Unable to authenticate user! Your credentials are invalid. Please double check and fix them.');
adapter.log.error('Error: Unable to authenticate user! Your credentials seem to be invalid. Please double check and fix them.');
adapter.log.error(`Credentials used for login: User:[${adapter.config.email}] - Password:[${adapter.config.Password}] - Country:[${adapter.config.country}]`);
break;
case 429: // endpoint currently not available
Expand All @@ -227,7 +239,7 @@ module.exports.getMqttCredentials = function(adapter) {
// Something happened in setting up the request that triggered an Error
adapter.log.error('[Error]: ' + error.message);
}
reject('Error during dyson API login:' + error );
reject('Error during dyson cloud API login:' + error );
});
});
};
Expand Down
14 changes: 13 additions & 1 deletion io-package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
{
"common": {
"name": "dysonairpurifier",
"version": "0.7.4",
"version": "0.7.5",
"news": {
"0.7.5": {
"en": "Fix: Adapter get online again after changes to dyson cloud API login procedure.",
"de": "Fix: Der Adapter wird nach Änderungen am Anmeldeverfahren der Dyson Cloud API wieder online geschaltet.",
"ru": "Исправлено: Адаптер снова выходит в сеть после изменений в процедуре входа в облачный API dyson.",
"pt": "Correção: o adaptador fica online novamente após alterações no procedimento de login da API do dyson cloud.",
"nl": "Oplossing: adapter gaat weer online na wijzigingen in de aanmeldingsprocedure van de dyson cloud API.",
"fr": "Correction: l'adaptateur est de nouveau en ligne après les modifications apportées à la procédure de connexion de l'API Dyson Cloud.",
"it": "Correzione: l'adattatore torna online dopo le modifiche alla procedura di accesso all'API del cloud di dyson.",
"es": "Solución: el adaptador vuelve a estar en línea después de los cambios en el procedimiento de inicio de sesión de la API en la nube de dyson.",
"pl": "Poprawka: Adapter jest ponownie online po zmianie procedury logowania dyson Cloud API.",
"zh-cn": "修复:更改dyson cloud API登录过程后,适配器再次联机。"
},
"0.7.4": {
"en": "Bugfix release for some minor bugs",
"de": "Bugfix Release für einige kleinere Fehler",
Expand Down
77 changes: 53 additions & 24 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,25 @@ class dysonAirPurifier extends utils.Adapter {
this.log.debug('onStateChange: Using dysonAction: [' + dysonAction + ']');
let messageData = {[dysonAction]: state.val};
switch (dysonAction) {
case 'Hostaddress' :
for (const mqttDevice in devices){
//noinspection JSUnresolvedVariable
if (devices[mqttDevice].Serial === thisDevice){
if (!state.val || typeof state.val === undefined || state.val === '') {
devices[mqttDevice].Hostaddress = thisDevice;
} else {
devices[mqttDevice].Hostaddress = state.val;
}
this.log.info(`Host address of device [${devices[mqttDevice].Serial}] has changed. Reconnecting with new address: [${devices[mqttDevice].Hostaddress}].`);
devices[mqttDevice].mqttClient = mqtt.connect('mqtt://' + devices[mqttDevice].Hostaddress, {
username: devices[mqttDevice].Serial,
password: devices[mqttDevice].mqttPassword,
protocolVersion: 3,
protocolId: 'MQIsdp'
});
}
}
break;
case 'fnsp' :
// when AUTO set AUTO to true also
if (state.val === 'AUTO') {
Expand All @@ -151,23 +170,26 @@ class dysonAirPurifier extends utils.Adapter {
break;
}
}
this.log.info('SENDING this data to device (' + thisDevice + '): ' + JSON.stringify(messageData));
// build the message to be send to the device
const message = {'msg': 'STATE-SET',
'time': new Date().toISOString(),
'mode-reason': 'LAPP',
'state-reason':'MODE',
'data': messageData
};
for (const mqttDevice in devices){
//noinspection JSUnresolvedVariable
if (devices[mqttDevice].Serial === thisDevice){
this.log.debug('MANUAL CHANGE: device [' + thisDevice + '] -> [' + action +'] -> [' + state.val + ']');
// only send to device if change should set a device value
if (action != 'Hostaddress'){
this.log.info('SENDING this data to device (' + thisDevice + '): ' + JSON.stringify(messageData));
// build the message to be send to the device
const message = {'msg': 'STATE-SET',
'time': new Date().toISOString(),
'mode-reason': 'LAPP',
'state-reason':'MODE',
'data': messageData
};
for (const mqttDevice in devices){
//noinspection JSUnresolvedVariable
devices[mqttDevice].mqttClient.publish(
devices[mqttDevice].ProductType + '/' + thisDevice + '/command',
JSON.stringify(message)
);
if (devices[mqttDevice].Serial === thisDevice){
this.log.debug('MANUAL CHANGE: device [' + thisDevice + '] -> [' + action +'] -> [' + state.val + ']');
//noinspection JSUnresolvedVariable
devices[mqttDevice].mqttClient.publish(
devices[mqttDevice].ProductType + '/' + thisDevice + '/command',
JSON.stringify(message)
);
}
}
}
} else if (state && state.ack) {
Expand Down Expand Up @@ -274,8 +296,9 @@ class dysonAirPurifier extends utils.Adapter {
}, device.Name);
this.log.debug('Querying Host-Address of device: ' + device.Serial);
const hostAddress = await this.getStateAsync(device.Serial + '.Hostaddress');
if (hostAddress && hostAddress.val !== '') {
this.log.debug('Found valid Host-Address.val [' + hostAddress.val + '] for device: ' + device.Serial);
this.log.debug('Got Host-Address-object [' + JSON.stringify(hostAddress) + '] for device: ' + device.Serial);
if (hostAddress && hostAddress.val &&hostAddress.val !== '') {
this.log.debug('Found valid Host-Address [' + hostAddress.val + '] for device: ' + device.Serial);
device.hostAddress = hostAddress.val;
this.createOrExtendObject(device.Serial + '.Hostaddress', {
type: 'state',
Expand All @@ -301,8 +324,6 @@ class dysonAirPurifier extends utils.Adapter {
},
native: {}
}, undefined);
adapter.log.info('No host address given. Trying to connect to the device with it\'s default hostname [' + device.Serial + ']. This should work if you haven\'t changed it and if you\'re running a DNS.');
device.hostAddress = device.Serial;
}
} catch(error){
this.log.error('[CreateOrUpdateDevice] Error: ' + error + ', Callstack: ' + error.stack);
Expand Down Expand Up @@ -613,12 +634,20 @@ class dysonAirPurifier extends utils.Adapter {
try {
const myAccount = await dysonUtils.getMqttCredentials(adapter);
if (typeof myAccount !== 'undefined'){
adapterLog.debug('Querying devices from dyson API.');
adapterLog.info('Querying devices from dyson API.');
devices = await dysonUtils.getDevices(myAccount, adapter);
for (const thisDevice in devices) {
await this.CreateOrUpdateDevice(devices[thisDevice]);
// Initializes the MQTT client for local communication with the thisDevice
adapterLog.info('Trying to connect to device [' + devices[thisDevice].Serial + '] via MQTT.');
if (!devices[thisDevice].hostAddress || devices[thisDevice].hostAddress === '' || devices[thisDevice].hostAddress === 'undefined' || typeof devices[thisDevice].hostAddress === undefined) {
adapter.log.info('No host address given. Trying to connect to the device with it\'s default hostname [' + devices[thisDevice].Serial + ']. This should work if you haven\'t changed it and if you\'re running a DNS.');
devices[thisDevice].hostAddress = devices[thisDevice].Serial;
}
// subscribe to changes on host address to re-init adapter on changes
this.log.debug('Subscribing for state changes on :' + devices[thisDevice].Serial + '.Hostaddress');
this.subscribeStates(devices[thisDevice].Serial + '.Hostaddress');
// connect to device
adapterLog.info(`Trying to connect to device [${devices[thisDevice].Serial}] via MQTT on host address [${devices[thisDevice].hostAddress}].`);
devices[thisDevice].mqttClient = mqtt.connect('mqtt://' + devices[thisDevice].hostAddress, {
username: devices[thisDevice].Serial,
password: devices[thisDevice].mqttPassword,
Expand All @@ -632,6 +661,7 @@ class dysonAirPurifier extends utils.Adapter {
devices[thisDevice].mqttClient.on('connect', function () {
//noinspection JSUnresolvedVariable
adapterLog.info(devices[thisDevice].Serial + ' - MQTT connection established.');
adapter.setDeviceOnlineState(devices[thisDevice].Serial, 'online');

// Subscribes to the status topic to receive updates
//noinspection JSUnresolvedVariable
Expand Down Expand Up @@ -692,8 +722,6 @@ class dysonAirPurifier extends utils.Adapter {
}
//noinspection JSUnresolvedVariable
adapterLog.debug(devices[thisDevice].Serial + ' - MQTT message received: ' + JSON.stringify(payload));
//noinspection JSUnresolvedVariable
adapter.setDeviceOnlineState(devices[thisDevice].Serial, 'online');
});

devices[thisDevice].mqttClient.on('error', function (error) {
Expand Down Expand Up @@ -792,6 +820,7 @@ class dysonAirPurifier extends utils.Adapter {
},
native: {}
}, state === 'online');
this.setState('info.connection', state === 'online');
}

/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "iobroker.dysonairpurifier",
"version": "0.7.4",
"version": "0.7.5",
"description": "ioBroker Adapter for dyson air purifiers and fans",
"author": {
"name": "Hanjo Hingsen",
Expand Down
7 changes: 4 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,17 @@ Which is what the dyson app does also.

### Todo
* detect IP of devices automatically
* collect more mqtt message acronym meanings
* subscribe changes of IP. OnChange reInit Adapter.
* Add symbols for each fan type in object-view like tradfri or alexa

### Known issues
* No automatic IP detection of devices
* Adapter is currently not able to detect whether a mqtt connection has been properly established or not.

## Changelog

### 0.7.5 (2021-02-12) (I won't surrender)
* (grizzelbee) Fix: [#65](https://github.com/Grizzelbee/ioBroker.dysonairpurifier/issues/65) Adapter get online again after changes to dyson cloud API login procedure.
* (grizzelbee) New: Adapter reconnects with new host address when it gets changed manually

### 0.7.4 (2021-02-10) (Human)
* (grizzelbee) Fix: fixed adapter traffic light for info.connection
* (grizzelbee) Fix: Minor fixes
Expand Down

0 comments on commit bd2ca16

Please sign in to comment.