Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

current Development #284

Merged
merged 3 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ All states report whether there is a failure or not. `True` means a failure, `fa

- (grizzelbee) Fix: [#281](https://github.com/Grizzelbee/ioBroker.dysonairpurifier/issues/281) Removed duplicate Sleeptimer field from config
- (grizzelbee) New: Enabled editing of field Sleeptimer
- (grizzelbee) Fix: [#283](https://github.com/Grizzelbee/ioBroker.dysonairpurifier/issues/283) Late config of fields
- (grizzelbee) Fix: Mapping text values in fields Sleeptimer & fanspeed to numerical values

### 3.1.9 (2024-05-13) (Marching on)

Expand Down
1 change: 1 addition & 0 deletions dyson-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,4 @@ module.exports.getFanIP = async function (self, deviceName) {
}
}
};

6 changes: 4 additions & 2 deletions dysonConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ const datapoints = new Map([
type: 'number',
writeable: true,
role: 'value',
unit: ''
unit: '',
displayValues: { 11: 'Auto' }
}
],
[
Expand Down Expand Up @@ -492,7 +493,8 @@ const datapoints = new Map([
type: 'number',
writeable: true,
role: 'value',
unit: 'Min'
unit: 'Min',
displayValues: { 0: 'Off' }
}
],
[
Expand Down
218 changes: 115 additions & 103 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,6 @@ class dysonAirPurifier extends utils.Adapter {
this.on('unload', this.onUnload.bind(this));
}

/**
* Quick hack to get 'temperatureUnit' working
* @returns {Partial<utils.AdapterOptions> & {temperatureUnit: 'K' | 'C' | 'F'}}
*/
// @ts-ignore
//get config() {
// return this.config;
// }

/**
* onMessage
*
Expand Down Expand Up @@ -606,7 +597,7 @@ class dysonAirPurifier extends utils.Adapter {
);
if (hostAddress?.val && typeof hostAddress.val === 'string') {
this.log.debug(
`Found valid Host-Address [${hostAddress.val}] for device: ${device.Serial}`
`Found predefined Host-Address [${hostAddress.val}] for device: ${device.Serial} in object tree.`
);
device.hostAddress = hostAddress.val;
this.createOrExtendObject(
Expand Down Expand Up @@ -732,12 +723,21 @@ class dysonAirPurifier extends utils.Adapter {
// TP02: When continuous monitoring is off and the fan is switched off - temperature and humidity loose their values.
// test whether the values are invalid and config.keepValues is true to prevent the old values from being destroyed
if (
dysonCode === 'rhtm' &&
message[dysonCode] === 'OFF' &&
// @ts-ignore
this.config.keepValues
) {
continue;
}
// if field is sleep timer test for value OFF and remap it to a number
if (dysonCode === 'sltm' && message[dysonCode] === 'OFF') {
value = 0;
}
// if field is fan speed test for value AUTO and remap it to a number
if (dysonCode === 'fnsp' && message[dysonCode] === 'AUTO') {
value = 11;
}
if (dysonCode === 'filf') {
// create additional data field filterlifePercent converting value from hours to percent; 4300 is the estimated lifetime in hours by dyson
this.createOrExtendObject(
Expand Down Expand Up @@ -1180,6 +1180,37 @@ class dysonAirPurifier extends utils.Adapter {
this.subscribeStates(`${device.Serial}.Sensor.PM25Index`);
}

async pollDeviceInfo(thisDevice, adapterLog) {
adapterLog.debug(
`Updating device [${thisDevice.Serial}] (polling API scheduled).`
);
try {
// possible messages:
// msg: 'REQUEST-CURRENT-STATE'
// msg: 'REQUEST-PRODUCT-ENVIRONMENT-CURRENT-SENSOR-DATA'
// msg: 'REQUEST-CURRENT-FAULTS'
// msg: 'REQUEST-CURRENT-STATE',
thisDevice.mqttClient.publish(
`${thisDevice.ProductType}/${thisDevice.Serial}/command`,
JSON.stringify({
msg: 'REQUEST-CURRENT-STATE',
time: new Date().toISOString()
})
);
thisDevice.mqttClient.publish(
`${thisDevice.ProductType}/${thisDevice.Serial}/command`,
JSON.stringify({
msg: 'REQUEST-CURRENT-FAULTS',
time: new Date().toISOString()
})
);
} catch (error) {
adapterLog.error(
`${thisDevice.Serial} - MQTT interval error: ${error}`
);
}
}

/**
* main
*
Expand Down Expand Up @@ -1208,33 +1239,15 @@ class dysonAirPurifier extends utils.Adapter {
this.log.debug(
`Result of CreateOrUpdateDevice: [${JSON.stringify(thisDevice)}]`
);
if (
!thisDevice.hostAddress ||
thisDevice.hostAddress === '' ||
thisDevice.hostAddress === 'undefined' ||
typeof thisDevice.hostAddress === 'undefined'
) {
this.log.info(
`No host address given. Trying to connect to the device with it's default hostname [${thisDevice.Serial}]. This should work if you haven't changed it and if you're running a DNS.`
);
thisDevice.hostAddress = thisDevice.Serial;
try {
thisDevice.ipAddress = await dysonUtils.getFanIP(
this,
thisDevice.Serial
);
} catch (err) {
this.log.error(err);
}
}
await this.getIPofDevice(thisDevice);
// subscribe to changes on host address to re-init adapter on changes
this.log.debug(
`Subscribing for state changes on datapoint: ${thisDevice.Serial}.hostAddress`
);
this.subscribeStates(`${thisDevice.Serial}.hostAddress`);
// connect to device
adapterLog.info(
`Trying to connect to device [${thisDevice.Serial}] via MQTT on host address [${thisDevice.hostAddress}@${thisDevice.ipAddress}].`
`${thisDevice.Serial} - MQTT connection requested for [${thisDevice.hostAddress}${thisDevice.ipAddress?`@${thisDevice.ipAddress}`:''}].`
);
thisDevice.mqttClient = mqtt.connect(
`mqtt://${thisDevice.hostAddress}`,
Expand All @@ -1245,46 +1258,33 @@ class dysonAirPurifier extends utils.Adapter {
protocolId: 'MQIsdp'
}
);

adapterLog.info(
`${thisDevice.Serial} - MQTT connection requested for [${thisDevice.hostAddress}@${thisDevice.ipAddress}].`
);

// Subscribes for events of the MQTT client
thisDevice.mqttClient.on('connect', function () {
/*******************************************************
* Subscribe to MQTT events
*******************************************************/
/****************
* Connect
****************/
thisDevice.mqttClient.on('connect', async function () {
adapterLog.info(
`${thisDevice.Serial} - MQTT connection established.`
);
adapter.setDeviceOnlineState(thisDevice.Serial, 'online');

// Subscribes to the status topic to receive updates

thisDevice.mqttClient.subscribe(
`${thisDevice.ProductType}/${thisDevice.Serial}/status/current`,
function () {
// Sends an initial request for the current state
thisDevice.mqttClient.publish(
`${thisDevice.ProductType}/${thisDevice.Serial}/command`,
JSON.stringify({
msg: 'REQUEST-CURRENT-STATE',
time: new Date().toISOString()
})
);
}
function () {}
);
// Sends an initial request for current state of device
await adapter.pollDeviceInfo(thisDevice, adapterLog);
// expect the adapter 20 seconds after first poll as Set-up
setTimeout(()=>{
adapterIsSetUp = true;
adapterLog.debug(`Device [${thisDevice.Serial}] is now set-up and config of datapoints is frozen.`);
}, 20000);
// Subscribes to the "faults" topic to receive updates on any faults and warnings
thisDevice.mqttClient.subscribe(
`${thisDevice.ProductType}/${thisDevice.Serial}/status/faults`,
function () {
// Sends an initial request for the current state
thisDevice.mqttClient.publish(
`${thisDevice.ProductType}/${thisDevice.Serial}/command`,
JSON.stringify({
msg: 'REQUEST-CURRENT-FAULTS',
time: new Date().toISOString()
})
);
}
function () {}
);
// Subscribes to the software topic to receive updates on any faults and warnings
thisDevice.mqttClient.subscribe(
Expand All @@ -1298,49 +1298,18 @@ class dysonAirPurifier extends utils.Adapter {
);
// Sets the interval for status updates
adapterLog.info(
// @ts-ignore
`Starting Polltimer with a ${adapter.config.pollInterval} seconds interval.`
);
// start refresh scheduler with interval from adapters config
thisDevice.updateIntervalHandle = setTimeout(function schedule() {
adapterLog.debug(
`Updating device [${thisDevice.Serial}] (polling API scheduled).`
);
try {
// possible messages:
// msg: 'REQUEST-PRODUCT-ENVIRONMENT-CURRENT-SENSOR-DATA'
// msg: 'REQUEST-CURRENT-FAULTS'
// msg: 'REQUEST-CURRENT-STATE',
thisDevice.mqttClient.publish(
`${thisDevice.ProductType}/${thisDevice.Serial}/command`,
JSON.stringify({
msg: 'REQUEST-CURRENT-STATE',
time: new Date().toISOString()
})
);
thisDevice.mqttClient.publish(
`${thisDevice.ProductType}/${thisDevice.Serial}/command`,
JSON.stringify({
msg: 'REQUEST-CURRENT-FAULTS',
time: new Date().toISOString()
})
);
} catch (error) {
adapterLog.error(
`${thisDevice.Serial} - MQTT interval error: ${error}`
);
}
// expect adapter has created all data points after first 20 secs of run.
setTimeout(() => {
adapterIsSetUp = true;
}, 20000);
thisDevice.updateIntervalHandle = setTimeout(
schedule,
// @ts-ignore
adapter.config.pollInterval * 1000
);
}, 10);
thisDevice.updateIntervalHandle = setTimeout( async () => {
await adapter.pollDeviceInfo(thisDevice, adapterLog);
},
adapter.config.pollInterval * 1000
);
});
/****************
* Message
****************/
thisDevice.mqttClient.on(
'message',
async function (_, payloadBuffer) {
Expand Down Expand Up @@ -1378,33 +1347,43 @@ class dysonAirPurifier extends utils.Adapter {
);
}
);

/****************
* Error
****************/
thisDevice.mqttClient.on('error', error => {
adapterLog.debug(`${thisDevice.Serial} - MQTT error: ${error}`);
adapter.setDeviceOnlineState(thisDevice.Serial, 'error');
});

/****************
* Re-Connect
****************/
thisDevice.mqttClient.on('reconnect', () => {
// @ts-ignore
if (!adapter.config.disableReconnectLogging)
adapterLog.info(`${thisDevice.Serial} - MQTT reconnecting.`);
adapter.setDeviceOnlineState(thisDevice.Serial, 'reconnect');
});

/****************
* Close
****************/
thisDevice.mqttClient.on('close', () => {
// @ts-ignore
if (!adapter.config.disableReconnectLogging)
adapterLog.info(`${thisDevice.Serial} - MQTT disconnected.`);
this.clearIntervalHandle(thisDevice.updateIntervalHandle);
adapter.setDeviceOnlineState(thisDevice.Serial, 'disconnected');
});

/****************
* Offline
****************/
thisDevice.mqttClient.on('offline', () => {
adapterLog.info(`${thisDevice.Serial} - MQTT offline.`);
this.clearIntervalHandle(thisDevice.updateIntervalHandle);
adapter.setDeviceOnlineState(thisDevice.Serial, 'offline');
});

/****************
* End
****************/
thisDevice.mqttClient.on('end', () => {
adapterLog.debug(`${thisDevice.Serial} - MQTT ended.`);
adapter.clearIntervalHandle(thisDevice.updateIntervalHandle);
Expand All @@ -1424,6 +1403,39 @@ class dysonAirPurifier extends utils.Adapter {
}
}

/**
* check whether there is a DNS that resolves the hostname
*
* @param {object} thisDevice link to the object of the device
* @returns {Promise<void>}
*/
async getIPofDevice(thisDevice) {
// todo not all DNS work with the same name structure e.g. fritzbox, bind9, pihole
// use iterations if there is no result by DNS request:
// 1. keep name like it is
// 2. replace underscores (if existent) by dashes (-)
// 4. replace dashes (if existent) by underscores (_)
if (
!thisDevice.hostAddress ||
thisDevice.hostAddress === '' ||
thisDevice.hostAddress === 'undefined' ||
typeof thisDevice.hostAddress === 'undefined'
) {
this.log.info(
`No host address given. Trying to connect to the device with it's default hostname [${thisDevice.Serial}]. This should work if you haven't changed it and if you're running a DNS.`
);
thisDevice.hostAddress = thisDevice.Serial;
try {
thisDevice.ipAddress = await dysonUtils.getFanIP(
this,
thisDevice.Serial
);
} catch (err) {
this.log.error(err);
}
}
}

/**
* onReady
*
Expand Down
Loading