Skip to content

Commit

Permalink
Add BL09XX/ESP32 temp devices
Browse files Browse the repository at this point in the history
  • Loading branch information
atrovato committed Jul 14, 2022
1 parent 931d701 commit 063fa01
Show file tree
Hide file tree
Showing 7 changed files with 408 additions and 3 deletions.
27 changes: 27 additions & 0 deletions server/services/tasmota/lib/features/device_temperature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const {
DEVICE_FEATURE_CATEGORIES,
DEVICE_FEATURE_TYPES,
DEVICE_FEATURE_UNITS,
} = require('../../../../utils/constants');

module.exports = {
// Tasmota matcher
keyMatcher: /^StatusSNS\.ESP32\.Temperature$/,
// Gladys feature
generateFeature: () => {
return {
category: DEVICE_FEATURE_CATEGORIES.DEVICE_TEMPERATURE_SENSOR,
type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL,
name: 'Device temperature',
read_only: true,
has_feedback: false,
min: -100,
max: 200,
unit: DEVICE_FEATURE_UNITS.CELSIUS,
};
},
generateExternalId: (key, fullKey) => {
const parts = fullKey.split('.').slice(1, -1);
return `${parts.join(':')}:DeviceTemperature`;
},
};
2 changes: 2 additions & 0 deletions server/services/tasmota/lib/features/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const colorTemperature = require('./colorTemperature');
const counter = require('./counter');
const humidity = require('./humidity');
const temperature = require('./temperature');
const deviceTemperature = require('./device_temperature');

const FEATURE_TEMPLATES = [
power,
Expand All @@ -33,6 +34,7 @@ const FEATURE_TEMPLATES = [
counter,
humidity,
temperature,
deviceTemperature,
];

const generateValue = (featureTemplate, value) => {
Expand Down
6 changes: 3 additions & 3 deletions server/services/tasmota/lib/features/temperature.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const {

module.exports = {
// Tasmota matcher
keyMatcher: /^StatusSNS\.(DHT11|AM2301)\.Temperature$/,
keyMatcher: /^StatusSNS\.(DHT11|AM2301|ENERGY\.BL09XX)\.Temperature$/,
// Gladys feature
generateFeature: () => {
return {
Expand All @@ -21,7 +21,7 @@ module.exports = {
};
},
generateExternalId: (key, fullKey) => {
const parts = fullKey.split('.');
return `${parts[1]}:Temperature`;
const parts = fullKey.split('.').slice(1, -1);
return `${parts.join(':')}:Temperature`;
},
};
46 changes: 46 additions & 0 deletions server/test/services/tasmota/lib/device-creation/BL09XX.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"STATUS": {
"Status": {
"ButtonRetain": 0,
"ButtonTopic": "0",
"FriendlyName": ["Tasmota"],
"LedMask": "FFFF",
"LedState": 1,
"Module": 18,
"Power": 0,
"PowerOnState": 1,
"PowerRetain": 0,
"SaveData": 1,
"SaveState": 1,
"SensorRetain": 0,
"SwitchMode": [0, 0, 0, 0, 0, 0, 0, 0],
"SwitchRetain": 0,
"SwitchTopic": "0",
"Topic": "tasmota"
}
},
"STATUS11": {
"StatusSTS": {
"MqttCount": 1,
"Sleep": 50,
"SleepMode": "Dynamic",
"Wifi": {
"AP": 1,
"BSSId": "EC:BE:DD:85:1F:E0",
"Channel": 1,
"LinkCount": 1,
"SSId": "ALEX-NETWORK"
}
}
},
"STATUS8": {
"StatusSNS": {
"ENERGY": {
"BL09XX": {
"Temperature": 23
}
},
"TempUnit": "C"
}
}
}
44 changes: 44 additions & 0 deletions server/test/services/tasmota/lib/device-creation/ESP32.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"STATUS": {
"Status": {
"ButtonRetain": 0,
"ButtonTopic": "0",
"FriendlyName": ["Tasmota"],
"LedMask": "FFFF",
"LedState": 1,
"Module": 18,
"Power": 0,
"PowerOnState": 1,
"PowerRetain": 0,
"SaveData": 1,
"SaveState": 1,
"SensorRetain": 0,
"SwitchMode": [0, 0, 0, 0, 0, 0, 0, 0],
"SwitchRetain": 0,
"SwitchTopic": "0",
"Topic": "tasmota"
}
},
"STATUS11": {
"StatusSTS": {
"MqttCount": 1,
"Sleep": 50,
"SleepMode": "Dynamic",
"Wifi": {
"AP": 1,
"BSSId": "EC:BE:DD:85:1F:E0",
"Channel": 1,
"LinkCount": 1,
"SSId": "ALEX-NETWORK"
}
}
},
"STATUS8": {
"StatusSNS": {
"ESP32": {
"Temperature": 23
},
"TempUnit": "C"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
const sinon = require('sinon');
const { expect } = require('chai');

const { fake, assert } = sinon;
const TasmotaHandler = require('../../../../../../../services/tasmota/lib');
const {
DEVICE_FEATURE_CATEGORIES,
DEVICE_FEATURE_TYPES,
DEVICE_FEATURE_UNITS,
EVENTS,
WEBSOCKET_MESSAGE_TYPES,
} = require('../../../../../../../utils/constants');

const messages = require('../../../device-creation/ESP32.json');

const mqttService = {
device: {
publish: fake.returns(null),
},
};
const gladys = {
event: {
emit: fake.returns(null),
},
stateManager: {
get: fake.returns(null),
},
};
const serviceId = 'service-uuid-random';

describe('Tasmota - MQTT - create device with ESP32 temperature feature', () => {
const tasmota = new TasmotaHandler(gladys, serviceId);
const tasmotaHandler = tasmota.protocols.mqtt;
tasmotaHandler.mqttService = mqttService;

beforeEach(() => {
sinon.reset();
});

it('decode STATUS message', () => {
tasmotaHandler.handleMessage('stat/tasmota-device-topic/STATUS', JSON.stringify(messages.STATUS));

expect(tasmotaHandler.discoveredDevices).to.deep.eq({});
expect(tasmotaHandler.pendingDevices).to.deep.eq({
'tasmota-device-topic': {
name: 'Tasmota',
params: [
{
name: 'protocol',
value: 'mqtt',
},
],
model: 18,
external_id: 'tasmota:tasmota-device-topic',
selector: 'tasmota-tasmota-device-topic',
service_id: serviceId,
should_poll: false,
features: [],
},
});

assert.notCalled(gladys.event.emit);
assert.notCalled(gladys.stateManager.get);
assert.calledWith(mqttService.device.publish, 'cmnd/tasmota-device-topic/STATUS', '11');
});

it('decode STATUS11 message', () => {
tasmotaHandler.handleMessage('stat/tasmota-device-topic/STATUS11', JSON.stringify(messages.STATUS11));

expect(tasmotaHandler.discoveredDevices).to.deep.eq({});
expect(tasmotaHandler.pendingDevices).to.deep.eq({
'tasmota-device-topic': {
name: 'Tasmota',
params: [
{
name: 'protocol',
value: 'mqtt',
},
],
model: 18,
external_id: 'tasmota:tasmota-device-topic',
selector: 'tasmota-tasmota-device-topic',
service_id: serviceId,
should_poll: false,
features: [],
},
});

assert.notCalled(gladys.event.emit);
assert.notCalled(gladys.stateManager.get);
assert.calledWith(mqttService.device.publish, 'cmnd/tasmota-device-topic/STATUS', '8');
});

it('decode STATUS8 message', () => {
tasmotaHandler.handleMessage('stat/tasmota-device-topic/STATUS8', JSON.stringify(messages.STATUS8));

const expectedDevice = {
name: 'Tasmota',
params: [
{
name: 'protocol',
value: 'mqtt',
},
],
model: 18,
external_id: 'tasmota:tasmota-device-topic',
selector: 'tasmota-tasmota-device-topic',
service_id: serviceId,
should_poll: false,
features: [
{
category: DEVICE_FEATURE_CATEGORIES.DEVICE_TEMPERATURE_SENSOR,
type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL,
unit: DEVICE_FEATURE_UNITS.CELSIUS,
external_id: 'tasmota:tasmota-device-topic:ESP32:DeviceTemperature',
selector: 'tasmota-tasmota-device-topic-esp32-devicetemperature',
name: 'Device temperature',
read_only: true,
has_feedback: false,
min: -100,
max: 200,
last_value: 23,
},
],
};
expect(tasmotaHandler.discoveredDevices).to.deep.eq({
'tasmota-device-topic': expectedDevice,
});
expect(tasmotaHandler.pendingDevices).to.deep.eq({});

assert.notCalled(mqttService.device.publish);
assert.calledWith(gladys.stateManager.get, 'deviceByExternalId', 'tasmota:tasmota-device-topic');

assert.calledWith(gladys.event.emit, EVENTS.DEVICE.NEW_STATE, {
device_feature_external_id: 'tasmota:tasmota-device-topic:ESP32:DeviceTemperature',
state: 23,
});
assert.calledWith(gladys.event.emit, EVENTS.WEBSOCKET.SEND_ALL, {
type: WEBSOCKET_MESSAGE_TYPES.TASMOTA.NEW_MQTT_DEVICE,
payload: expectedDevice,
});
});
});
Loading

0 comments on commit 063fa01

Please sign in to comment.