Skip to content

Commit

Permalink
Merge pull request #233 from Garfonso/dev
Browse files Browse the repository at this point in the history
v2.0.1 & dependency updates
  • Loading branch information
GermanBluefox authored Jul 23, 2021
2 parents 786261e + 74b73fe commit fecd326
Show file tree
Hide file tree
Showing 27 changed files with 8,550 additions and 268 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG_OLD.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Older Changes

## 1.4.3 (2021-02-01)
* (bluefox) Support of lovelace via ioBroker.pro

## 1.4.2 (2021-01-08)
* (thost96) Fixed: set Theme state type to string instead of text

## 1.4.1 (2021-01-08)
* (bluefox) Support of new Let's Encrypt (only with js-controller 3.2.x)

## 1.3.6 (2021-01-08)
* (Garfonso) Fixed: do not ignore devices deleted from iot / without smartName
* (Garfosno) Added: Support location devices with one GPS state in string form
Expand Down
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,20 @@ After that checkout modified version in `./build` folder. Then.
PLACEHOLDER for next version:
### **WORK IN PROGRESS**
-->
### **WORK IN PROGRESS**
* (Garfonso) Add support for fan entity (makes fan card working).
* (Garfonso) fix backwards compatibility for input_datetime attributes.
* (Garfonso) fix airconditioner with power mode on mode update

### 2.0.3 (2021-07-04)
* (Garfonso) fix typo.

### 2.0.2 (2021-07-04)
* (Garfonso) fixed: Date shift for weather forecast without date state.

### 2.0.1 (2021-07-01)
* (Garfonso) fixed: Zigbee lights (issue 222).

### 2.0.0 (2021-06-17)
* (Garfonso) Changed: !Breaking! Battery warning is now binary_sensor instead of sensor (now ui sets icon and translates ok)
* (Garfonso) Fixed: !Breaking! entity_id conflict for low_bat / humidity when part of another device
Expand Down Expand Up @@ -495,15 +509,6 @@ After that checkout modified version in `./build` folder. Then.
* (Garfonso) Fixed: default themes do not show as selected
* (Garfonso) Fixed: Loading themes / custom cards / image-proxy

### 1.4.3 (2021-02-01)
* (bluefox) Support of lovelace via ioBroker.pro

### 1.4.2 (2021-01-08)
* (thost96) Fixed: set Theme state type to string instead of text

### 1.4.1 (2021-01-08)
* (bluefox) Support of new Let's Encrypt (only with js-controller 3.2.x)

## License

Copyright 2019-2021, bluefox <[email protected]>
Expand Down
8 changes: 6 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ gulp.task('translateAndUpdateWordsJS', gulp.series('translate', 'adminLanguages2
gulp.task('default', gulp.series('updatePackages', 'updateReadme'));

const devServerPath = __dirname + '/.dev-server/default/';
const devserverIoBrokerPath = devServerPath + 'node_modules/iobroker.js-controller/iobroker.js';
const spawn = require('child_process').spawn;
async function spawnChild(command, params, logmsg, local) {
if (logmsg) {
Expand All @@ -594,7 +595,6 @@ async function spawnChild(command, params, logmsg, local) {
}
gulp.task('prepareDevserver', async done => {
const promises = [];
const devserverIoBrokerPath = devServerPath + 'node_modules/iobroker.js-controller/iobroker.js';
filesWalk(__dirname + '/test/testData', (fileName) => {
if (fileName && fileName.toLowerCase().endsWith('.json')) {
const objects = JSON.parse(fs.readFileSync(fileName));
Expand All @@ -616,7 +616,11 @@ gulp.task('updateDevserver', async done => {
await spawnChild(npmCmd, ['install', 'iobroker.devices@latest'], 'Updating devices');
await spawnChild(npmCmd, ['install', 'iobroker.history@latest'], 'Updating history');
await spawnChild(npmCmd, ['install', 'iobroker.type-detector@latest'], 'Updating type-detector');
await spawnChild('node', [devserverIoBrokerPath, 'upload', 'devices'], 'Uploading devices');
await spawnChild('node', [devserverIoBrokerPath, 'upload', 'history'], 'Uploading history');
await spawnChild('node', [devserverIoBrokerPath, 'upload', 'admin'], 'Uploading admin');
await spawnChild(npmCmd, ['link', 'iobroker.lovelace'], 'Linking lovelace');
await spawnChild(npmCmd, ['install'], 'Reparing dependencies in lovelace', true);
await spawnChild('node', [devserverIoBrokerPath, 'upload', 'lovelace'], 'Uploading lovelace');
await spawnChild(npmCmd, ['install'], 'Repairing dependencies in lovelace', true);
done();
});
76 changes: 38 additions & 38 deletions io-package.json

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions lib/converters/binary_sensor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
const utils = require('./utils');

function createSensorEntity(control, name, room, func, _obj, forcedEntityId, stateName = 'ACTUAL') {
const entity = utils.processCommon(name, room, func, _obj, 'binary_sensor', forcedEntityId);

entity.context.STATE = {getId: null};
const state = control.states.find(s => s.id && s.name === stateName);
if (state && state.id) {
entity.context.STATE.getId = state.id;
utils.addID2entity(state.id, entity);
}
return entity;
}

exports.processMotion = function (id, control, name, room, func, _obj, objects, forcedEntityId) {
const entity = utils.processCommon(name, room, func, _obj, 'binary_sensor', forcedEntityId);

Expand Down Expand Up @@ -59,6 +71,12 @@ exports.processBattery = function (control, name, room, func, objects, forcedEnt
}
};

exports.processFireAlarm = function (id, control, name, room, func, _obj, objects, forcedEntityId) {
const entity = createSensorEntity(control, name, room, func, _obj, forcedEntityId);
entity.attributes.device_class = 'smoke';
return [entity];
};

/**
* Create manual binary_sensor entity.
* @param id - id of "main" object, i.e. state.
Expand Down
4 changes: 2 additions & 2 deletions lib/converters/climate.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ function fillClimateEntityFromStates(states, objects, entity, iobType) {
state = state || {val: null};
entity.context.iobMode = state.val;
entity.attributes.hvac_mode = attr.iobToLovelace[state.val] || state.val;
if (!state.val && ((states.state || states.stateRead) && entity.state === 'off')) {
entity.attributes.hvac_mode = entity.state;
if (!entity.context.iobPower && ((states.state || states.stateRead) && entity.state === 'off')) {
entity.attributes.hvac_mode = entity.state; //stay in off mode, if power mode and power is off.
} else {
entity.state = entity.attributes.hvac_mode;
}
Expand Down
136 changes: 136 additions & 0 deletions lib/converters/fan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
const utils = require('./utils');
const adapterData = require('./../dataSingleton');

async function fillEntityFromStates(states, objects, entity) {
//'old' fan:
// "modes": {
// "0": "off",
// "1": "low",
// "2": "medium",
// "3": "high"
// }
// -> use either states or this as default.
// 'new' fan:
// preset_mode instead of speed. Can support more modes...
// preset_mode -> will call set_preset_mode.
// support both, we don't care.


utils.fillEntityFromStates(states, entity); //already prefills attributes.

//preset_mode is important:
if (states.preset_mode) {
const attr = entity.context.ATTRIBUTES.find(a => a.attribute === 'preset_mode');

const obj = objects[states.preset_mode] || {common: {type: 'number'}};
if (!obj.common) {
obj.common = {type: 'number', states: {'0': 'off', '1': 'low', '2': 'medium', '3': 'high'}};
}
if (!obj.common.type) {
obj.common.type = 'number';
}
if (!obj.common.states) {
obj.common.states = {'0': 'off', '1': 'low', '2': 'medium', '3': 'high'};
}

if (obj.common.states) {
if (obj.common.states instanceof Array) {
attr.isStringArray = true;
entity.attributes.preset_modes = obj.common.states;
} else {
if (typeof obj.common.states === 'string') {
adapterData.log.warn(obj._id + ': states is of type string. Problems might occur. Please fix states to be of type object.');
attr.map2lovelace = {};
for (const kv of obj.common.states.split(';')) {
const [key, value] = kv.split(':');
attr.map2lovelace[key] = value;
}
} else {
attr.map2lovelace = obj.common.states;
}
attr.isNumber = obj.common.type && obj.common.type.toLowerCase() === 'number';
attr.map2iob = {};
entity.attributes.preset_modes = [];
Object.keys(attr.map2lovelace).forEach(k => {
attr.map2iob[attr.map2lovelace[k]] = k;
entity.attributes.preset_modes.push(attr.map2lovelace[k]);
});
}
}

attr.getParser = (entity, attr, state) => {
state = state || {val: null};
entity.attributes.speed = state.val || 'unknown';
if (attr.map2lovelace) {
entity.attributes.speed = attr.map2lovelace[state.val] || state.val || 'unknown';
}
entity.attributes.preset_mode = entity.attributes.speed;
entity.state = entity.attributes.speed !== 'off' ? 'on' : 'off';
};

if (!entity.context.COMMANDS) {
entity.context.COMMANDS = [];
}
const parseCommand = async (entity, command, data, user) => {
let target = data.service_data.speed || data.service_data.preset_mode;
if (!attr.isStringArray) {
if (attr.map2iob) {
target = attr.map2iob[target];
}
if (attr.isNumber) {
target = Number(target);
}
}
if (!target && target !== 0) {
target = data.service_data.speed || data.service_data.preset_mode; //fallback
}

return adapterData.adapter.setForeignStateAsync(command.setId, target, false, {user});
};

entity.context.COMMANDS.push({
service: 'set_speed',
setId: states.preset_mode,
parseCommand
});
entity.context.COMMANDS.push({
service: 'set_preset_mode',
setId: states.preset_mode,
parseCommand
});
entity.context.COMMANDS.push({
service: 'turn_off',
setId: states.preset_mode,
parseCommand: async (entity, command, data, user) => {
await parseCommand(entity, command, {service_data: {preset_mode: 'off'}}, user);
if (states.state) {
await adapterData.adapter.setForeignStateAsync(states.state, false, false, {user});
}
}
});
}

return [entity];
}

/**
* Create manual input_select entity.
* @param id - id of "main" object, i.e. state.
* @param obj - iobroker object of id param
* @param entity - already created entity
* @param objects - id object cache
* @param custom - custom part of object
* @returns {Promise<[entity]>}
*/
exports.processManualEntity = async function(id, obj, entity, objects, custom) {
const states = custom.states || {
preset_mode: id
};
if (!states.preset_mode) {
states.speed = states.speed || id;
}
delete states.speed; //want only one attribute.


return fillEntityFromStates(states, objects, entity);
};
12 changes: 10 additions & 2 deletions lib/converters/input_datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ function fillEntityFromStates(states, objects, entity) {
const parts = data.time.split(':');
date.setHours(parseInt(parts[0], 10));
date.setMinutes(parseInt(parts[1], 10));
date.setSeconds(parts[2] ? parseInt(parts[2], 10) : 0);
date.setMilliseconds(0);
}
targetData = date.getTime();
}
Expand Down Expand Up @@ -79,7 +81,13 @@ exports.processManualEntity = async function(id, obj, entity, objects, custom) {
state: id
};

entity.attributes.has_time = custom.attr_has_time || false || (custom.attr_has_time === undefined && custom.attr_has_time);
entity.attributes.has_date = (custom.attr_has_date === undefined && custom.has_date === undefined) ? true : custom.attr_has_date || custom.has_date; //date on by default.
entity.attributes.has_time = custom.attr_has_time || false;
if (custom.attr_has_time === undefined) {
entity.attributes.has_time = custom.has_time || false;
}
entity.attributes.has_date = custom.attr_has_date;
if (custom.attr_has_date === undefined) {
entity.attributes.has_date = custom.has_date === undefined ? true : custom.has_date; //date on by default.
}
return fillEntityFromStates(states, objects, entity);
};
4 changes: 2 additions & 2 deletions lib/converters/light.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ function _getLightAdvancedState(control) {
function _lightAdvancedAddState(states, objects, entity) {
const getState = states.stateRead;
//prevent zigbee 'available' to become getId:
if (getState && getState.indexOf('zigbee.') === 0 && getState.indexOf('.available') > 0) {
entity.context.STATE.getId = states.states;
if (getState && getState.startsWith('zigbee.') && (getState.endsWith('.available') || getState.endsWith('.device_query'))) {
entity.context.STATE.getId = states.state;
}
if (states.state) {
entity.context.STATE.isBoolean = objects[states.state] && objects[states.state].common && objects[states.state].common.type === 'boolean';
Expand Down
6 changes: 4 additions & 2 deletions lib/converters/switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ exports.processSocket = function (id, control, name, room, func, _obj, objects,

state = control.states.find(s => s.id && s.name === 'ACTUAL');
if (state && state.id) {
entity.context.STATE.getId = state.id;
utils.addID2entity(state.id, entity);
if (!state.id.startsWith('zigbee.') || !state.id.endsWith('.available') || !state.id.endsWith('.device_query')) {
entity.context.STATE.getId = state.id;
utils.addID2entity(state.id, entity);
}
}

return [entity];
Expand Down
Loading

0 comments on commit fecd326

Please sign in to comment.