Compare commits

...
39 Commits
Author SHA1 Message Date
Koenkk c8c8417192 0.1.3 2018-08-17 18:32:11 +02:00
Koen Kanters 56a0f6256b Merge pull request #287 from tb-killa/logfile-extended
Logfile extended
2018-08-16 20:12:07 +02:00
Koenkk 197a3cdebc Update log file extension. 2018-08-16 20:04:29 +02:00
tb-killa c520985a4f Implement cleanup-function for logfiles 2018-08-16 19:07:32 +02:00
tb-killa 931926cd96 add fs and bring in advanced logname with dateformat 2018-08-16 19:07:32 +02:00
tb-killa 427c99cd83 add moment to logger.js 2018-08-16 19:06:48 +02:00
tb-killa c2cd02e790 add moment to package.json 2018-08-16 19:06:48 +02:00
Koenkk a921394240 Merge branch 'dev' of https://github.com/Koenkk/zigbee2mqtt into dev 2018-08-16 18:56:51 +02:00
Koenkk 21f047f4aa Use localtimestamp for file logging. #289 2018-08-16 18:56:33 +02:00
Koenkk 9a70c95672 Start adding tests. 2018-08-15 22:19:54 +02:00
Koenkk fc2e93d96f Update dependencies 2018-08-15 21:10:19 +02:00
Koenkk 494c15a731 Update dependencies. 2018-08-13 20:37:01 +02:00
Koen Kanters 23a2feb7cf Merge pull request #275 from redmusicxd/patch-1
Trust ZLED-2709
2018-08-13 19:22:39 +02:00
redmusicxd f4ceccaa9b Trust ZLED-2709 2018-08-13 19:22:24 +02:00
Koenkk b4398e21c3 Fix linting 2018-08-13 19:21:55 +02:00
tb-killa 3900d56d6e Extend ping function with friendlyName logging 2018-08-13 19:21:55 +02:00
tb-killa 08f64b96be remove arrow separator 2018-08-13 19:21:55 +02:00
tb-killa 8411c0b30d Change logger-formatter confirm to zigbee-shepherd debug 2018-08-13 19:21:55 +02:00
Koenkk 43260a3a83 Implement mqtt last will message. #267 2018-08-13 19:21:55 +02:00
Koenkk 6f4013fd94 Update zigbee-shepherd-converters. https://github.com/Koenkk/zigbee2mqtt/issues/204 2018-08-13 19:21:55 +02:00
Koenkk 426a89914f Update zigbee-shepherd. #252 2018-08-13 19:21:55 +02:00
tb-killa 4fa1a4c57a Extend configureDevice with friendlyName Output 2018-08-13 19:21:55 +02:00
Koenkk 5ddfcea8f3 Log coordinator firmware version on startup. 2018-08-13 19:21:55 +02:00
Koenkk 502c0cb852 Log error when zigbee command fails. #254 2018-08-13 19:21:55 +02:00
Koenkk 07d8fd5605 Update zigbee-shepherd. 2018-08-13 19:21:55 +02:00
Daniel Welch 445ccb5bbd Update trigger-downstream.sh to dev branch
https://github.com/Koenkk/zigbee-shepherd-converters/pull/46#issuecomment-410533163
@Koenkk
The problem here was that this script is triggered with pushes to `master`, not `dev`. I've updated the edge add-on version to use the `dev` branch rather than `master`. A new image is building now and should be available soon using the dev branch.
2018-08-13 19:21:55 +02:00
Koenkk 74c8758a3b Update zigbee-shepherd. 2018-08-13 19:21:55 +02:00
Koenkk 1f6f73e8d2 Allow to log to custom directory. #207 #182 2018-08-13 19:21:55 +02:00
Koenkk 04b2f24d6b Persist devices state across restarts. #249 2018-08-13 19:21:55 +02:00
Alex Mekkering 5e96c46a7a Update to serialport 6.2.2 2018-08-13 19:21:55 +02:00
Koenkk ec4f5e1472 Update dependencies. 2018-08-13 19:21:55 +02:00
Koenkk 32a512ea6b Update zcl-packet. #150 2018-08-13 19:21:55 +02:00
Koenkk 46a3461e76 Update dependencies 2018-08-13 19:21:55 +02:00
Koenkk fe92e9c421 Fix package.json 2018-08-13 19:21:55 +02:00
Koenkk 2f02168fcb Update to latest zcl-packet. #150 #198 2018-08-13 19:21:55 +02:00
Koenkk 2ba86a13eb Add linkquality to messages. #173 2018-08-13 19:21:55 +02:00
Koen Kanters 7dd1c75be0 Update issue template. #264 2018-08-09 17:56:51 +02:00
Koen Kanters b5f0e9b958 Update ISSUE_TEMPLATE.md 2018-08-08 19:05:22 +02:00
Koenkk bd901ec9dd Add issue template. #264 2018-08-08 19:04:24 +02:00
15 changed files with 4631 additions and 1574 deletions
+7 -5
View File
@@ -4,14 +4,16 @@
},
"extends": ["eslint:recommended", "google"],
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
},
"ecmaVersion": 6,
"sourceType": "module"
},
"rules": {
"require-jsdoc": "off",
"indent": ["error", 4],
"max-len": ["error", { "code": 120 }]
}
"max-len": ["error", { "code": 120 }],
"mocha/no-exclusive-tests": "error"
},
"plugins": [
"mocha"
]
}
+11
View File
@@ -0,0 +1,11 @@
Unsupported device
First check: https://github.com/Koenkk/zigbee2mqtt/wiki/How-to-support-new-devices
Bug report
- First make sure your on the latest version of zigbee2mqtt.
- Make sure you are running the latest CC253X firmware from https://github.com/koenkk/z-stack-firmware.
- Provide a clear description of the problem.
- If applicable, provide steps how to reproduce the problem.
- Provide a log with `log_level` set to `debug`, see https://github.com/Koenkk/zigbee2mqtt/wiki/Configuration
- DON'T copy logs directly here, post a link to https://hastebin.com/ or https://pastebin.com/.
+3 -1
View File
@@ -60,4 +60,6 @@ typings/
# data
data/database.db
data/config.json
data/log*.txt
data/log*.txt
data/state.json
data/log
+3 -2
View File
@@ -11,11 +11,12 @@ node_js:
install:
- npm install
script:
before_script:
- npm test
- npm run verify-homeassistant-mapping
- npm run eslint
after_success:
script:
- ".travis/docker.sh"
- ".travis/wiki.sh"
- ".travis/trigger-downstream.sh"
+3 -3
View File
@@ -20,11 +20,11 @@ function travis_trigger() {
"https://api.travis-ci.org/repo/${org}%2F${repo}/requests"
}
# Only trigger downstream if on master branch and not pull request
if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]
# Only trigger downstream if on dev branch and not pull request
if [ "$TRAVIS_BRANCH" = "dev" -a "$TRAVIS_PULL_REQUEST" = "false" ]
then
echo "Triggering build of downstream projects!"
travis_trigger "danielwelch" "hassio-zigbee2mqtt" "master"
else
echo "Not triggering build of downstream projects, triggered by pull request or not on master branch"
echo "Not triggering build of downstream projects, triggered by pull request or not on dev branch"
fi
+22 -8
View File
@@ -1,5 +1,6 @@
const MQTT = require('./mqtt');
const Zigbee = require('./zigbee');
const State = require('./state');
const logger = require('./util/logger');
const settings = require('./util/settings');
const zigbeeShepherdConverters = require('zigbee-shepherd-converters');
@@ -32,7 +33,7 @@ class Controller {
constructor() {
this.zigbee = new Zigbee();
this.mqtt = new MQTT();
this.stateCache = {};
this.state = new State();
this.configured = [];
this.handleZigbeeMessage = this.handleZigbeeMessage.bind(this);
this.handleMQTTMessage = this.handleMQTTMessage.bind(this);
@@ -99,8 +100,8 @@ class Controller {
sendAllCachedStates() {
this.zigbee.getAllClients().forEach((device) => {
if (this.stateCache.hasOwnProperty(device.ieeeAddr)) {
this.mqttPublishDeviceState(device.ieeeAddr, this.stateCache[device.ieeeAddr], false);
if (this.state.exists(device.ieeeAddr)) {
this.mqttPublishDeviceState(device.ieeeAddr, this.state.get(device.ieeeAddr), false);
}
});
}
@@ -152,6 +153,7 @@ class Controller {
}
stop(callback) {
this.state.save();
this.mqtt.disconnect();
this.pollTimer(false);
this.softResetTimeout(false);
@@ -159,7 +161,11 @@ class Controller {
}
configureDevice(device) {
let friendlyName = 'unknown';
const ieeeAddr = device.ieeeAddr;
if (settings.getDevice(ieeeAddr)) {
friendlyName = settings.getDevice(ieeeAddr).friendly_name;
}
if (ieeeAddr && device.modelId && !this.configured.includes(ieeeAddr)) {
const mappedModel = zigbeeShepherdConverters.findByZigbeeModel(device.modelId);
@@ -167,9 +173,9 @@ class Controller {
if (mappedModel && mappedModel.configure) {
mappedModel.configure(ieeeAddr, this.zigbee.shepherd, this.zigbee.getCoordinator(), (ok, msg) => {
if (ok) {
logger.info(`Succesfully configured ${ieeeAddr}`);
logger.info(`Succesfully configured ${friendlyName} ${ieeeAddr}`);
} else {
logger.error(`Failed to configure ${ieeeAddr}`);
logger.error(`Failed to configure ${friendlyName} ${ieeeAddr}`);
}
});
}
@@ -312,6 +318,11 @@ class Controller {
}
});
// Add device linkquality.
if (message.linkquality) {
payload.linkquality = message.linkquality;
}
this.mqttPublishDeviceState(device.ieeeAddr, payload, cache);
};
@@ -388,6 +399,9 @@ class Controller {
// Remove from configuration.yaml
settings.removeDevice(deviceID);
// Remove from state
this.state.remove(deviceID);
logger.info(`Successfully removed ${deviceID}`);
this.mqtt.log('device_removed', message);
};
@@ -544,13 +558,13 @@ class Controller {
mqttPublishDeviceState(deviceID, payload, cache) {
if (cacheState) {
// Add cached state to payload
if (this.stateCache[deviceID]) {
payload = objectAssignDeep.noMutate(this.stateCache[deviceID], payload);
if (this.state.exists(deviceID)) {
payload = objectAssignDeep.noMutate(this.state.get(deviceID), payload);
}
// Update state cache with new state.
if (cache) {
this.stateCache[deviceID] = payload;
this.state.set(deviceID, payload);
}
}
+25 -18
View File
@@ -191,28 +191,21 @@ const configurations = {
command_topic: true,
},
},
'switch_left': {
};
const switchWithPostfix = (postfix) => {
return {
type: 'switch',
object_id: 'switch_left',
object_id: `switch_${postfix}`,
discovery_payload: {
payload_off: 'OFF',
payload_on: 'ON',
value_template: '{{ value_json.state_left }}',
value_template: `{{ value_json.state_${postfix} }}`,
command_topic: true,
command_topic_prefix: 'left',
command_topic_prefix: postfix,
json_attributes: [`button_${postfix}`],
},
},
'switch_right': {
type: 'switch',
object_id: 'switch_right',
discovery_payload: {
payload_off: 'OFF',
payload_on: 'ON',
value_template: '{{ value_json.state_right }}',
command_topic: true,
command_topic_prefix: 'right',
},
},
};
};
// Map homeassitant configurations to devices.
@@ -223,7 +216,7 @@ const mapping = {
'WXKG03LM': [configurations.sensor_click],
'WXKG02LM': [configurations.sensor_click],
'QBKG04LM': [configurations.switch],
'QBKG03LM': [configurations.switch_left, configurations.switch_right],
'QBKG03LM': [switchWithPostfix('left'), switchWithPostfix('right')],
'WSDCGQ01LM': [configurations.sensor_temperature, configurations.sensor_humidity],
'WSDCGQ11LM': [configurations.sensor_temperature, configurations.sensor_humidity, configurations.sensor_pressure],
'RTCGQ01LM': [configurations.binary_sensor_occupancy],
@@ -281,7 +274,7 @@ const mapping = {
'SL 110 W': [configurations.light_brightness],
'AA68199': [configurations.light_brightness_colortemp],
'QBKG11LM': [configurations.switch, configurations.sensor_power],
'QBKG12LM': [configurations.switch_left, configurations.switch_right, configurations.sensor_power],
'QBKG12LM': [switchWithPostfix('left'), switchWithPostfix('right'), configurations.sensor_power],
'K2RGBW01': [configurations.light_brightness_colortemp_xy],
'9290011370': [configurations.light_brightness],
'DNCKATSW001': [configurations.switch],
@@ -298,6 +291,20 @@ const mapping = {
'E11-G23': [configurations.light_brightness],
'AC03845': [configurations.light_brightness_colortemp_xy],
'AC03641': [configurations.light_brightness],
'FB56+ZSW05HG1.2': [configurations.switch],
'72922-A': [configurations.switch],
'AC03642': [configurations.light_brightness_colortemp],
'DNCKATSW002': [switchWithPostfix('left'), switchWithPostfix('right')],
'DNCKATSW003': [switchWithPostfix('left'), switchWithPostfix('right'), switchWithPostfix('center')],
'DNCKATSW004': [
switchWithPostfix('bottom_left'), switchWithPostfix('bottom_right'),
switchWithPostfix('top_left'), switchWithPostfix('top_right'),
],
'BY 165': [configurations.light_brightness],
'ZLED-2709': [configurations.light_brightness],
'8718696548738': [configurations.light_brightness_colortemp],
'4052899926110': [configurations.light_brightness_colortemp_xy],
'Z01-CIA19NAE26': [configurations.light_brightness],
};
// A map of all discoverd devices
+14 -3
View File
@@ -12,7 +12,14 @@ class MQTT {
const mqttSettings = settings.get().mqtt;
logger.info(`Connecting to MQTT server at ${mqttSettings.server}`);
const options = {};
const options = {
will: {
topic: this.prefixTopic('bridge/state'),
payload: 'offline',
retain: true,
},
};
if (mqttSettings.user && mqttSettings.password) {
options.username = mqttSettings.user;
options.password = mqttSettings.password;
@@ -68,8 +75,7 @@ class MQTT {
}
publish(topic, payload, options, callback, baseTopic) {
baseTopic = baseTopic ? baseTopic : settings.get().mqtt.base_topic;
topic = `${baseTopic}/${topic}`;
topic = this.prefixTopic(topic, baseTopic);
if (!this.client || this.client.reconnecting) {
logger.error(`Not connected to MQTT server!`);
@@ -81,6 +87,11 @@ class MQTT {
this.client.publish(topic, payload, options, callback);
}
prefixTopic(topic, baseTopic) {
baseTopic = baseTopic ? baseTopic : settings.get().mqtt.base_topic;
return `${baseTopic}/${topic}`;
}
log(type, message) {
const payload = {type: type, message: message};
this.publish('bridge/log', JSON.stringify(payload), {retain: false});
+55
View File
@@ -0,0 +1,55 @@
const logger = require('./util/logger');
const data = require('./util/data');
const fs = require('fs');
const saveInterval = 1000 * 60 * 5; // 5 minutes
class State {
constructor() {
this.state = {};
this.file = data.joinPath('state.json');
this._load();
// Save the state on every interval
this.timer = setInterval(() => this.save(), saveInterval);
}
_load() {
if (fs.existsSync(this.file)) {
try {
this.state = JSON.parse(fs.readFileSync(this.file, 'utf8'));
logger.debug(`Loaded state from file ${this.file}`);
} catch (e) {
logger.debug(`Failed to load state from file ${this.file} (corrupt file?)`);
}
} else {
logger.debug(`Can't load state from file ${this.file} (doesn't exsist)`);
}
}
save() {
logger.debug(`Saving state to file ${this.file}`);
const json = JSON.stringify(this.state, null, 4);
fs.writeFileSync(this.file, json, 'utf8');
}
exists(ID) {
return this.state.hasOwnProperty(ID);
}
get(ID) {
return this.state[ID];
}
set(ID, state) {
this.state[ID] = state;
}
remove(ID) {
if (this.exists(ID)) {
delete this.state[ID];
}
}
}
module.exports = State;
+1
View File
@@ -11,4 +11,5 @@ if (process.env.ZIGBEE2MQTT_DATA) {
module.exports = {
joinPath: (file) => path.join(dataPath, file),
getPath: () => dataPath,
};
+46 -14
View File
@@ -1,37 +1,69 @@
const winston = require('winston');
const moment = require('moment');
const data = require('./data');
const settings = require('./settings');
const path = require('path');
const fs = require('fs');
const fx = require('mkdir-recursive');
const rimraf = require('rimraf');
let logLevel = '';
// Determine the log level.
let level = winston.level.info;
if (process.env.DEBUG) {
logLevel = 'debug';
level = 'debug';
} else if (settings.get().advanced && settings.get().advanced.log_level) {
logLevel = settings.get().advanced.log_level;
} else {
logLevel = winston.level.info;
level = settings.get().advanced.log_level;
}
// Directoy to log to
let rootDirectory = path.join(data.getPath(), 'log');
if (settings.get().advanced && settings.get().advanced.log_directory) {
rootDirectory = settings.get().advanced.log_directory;
}
// Add start time to directory.
const directory = path.join(rootDirectory, moment(Date.now()).format('YYYY-MM-DD.HH:mm:ss'));
// Make sure that log directoy exsists
fx.mkdirSync(directory);
// Create logger
const logger = new (winston.Logger)({
transports: [
new (winston.transports.File)({
filename: data.joinPath('log.txt'),
filename: path.join(directory, 'log.txt'),
json: false,
level: logLevel,
maxFiles: 3,
maxsize: 10000000, // 10MB
level: level,
maxFiles: 3, // Max 3 files per run.
maxsize: 10000000, // 10MB // Only if Filename is static!
timestamp: () => new Date().toLocaleString(),
}),
new (winston.transports.Console)({
timestamp: () => new Date().toLocaleString(),
formatter: function(options) {
return options.timestamp() + ' ' +
winston.config.colorize(options.level, options.level.toUpperCase()) + ' ' +
(options.message ? options.message : '') +
(options.meta && Object.keys(options.meta).length ? '\n\t'+ JSON.stringify(options.meta) : '' );
return winston.config.colorize(options.level, ' zigbee2mqtt:' + options.level.toLowerCase()) + ' ' +
options.timestamp() + ' ' + (options.message ? options.message : '') +
(options.meta && Object.keys(options.meta).length ? '\n\t'+ JSON.stringify(options.meta) : '' );
},
}),
],
});
logger.transports.console.level = logLevel;
logger.info(`Logging to directory: '${directory}'`);
logger.transports.console.level = level;
// Cleanup any old log directory.
let directories = fs.readdirSync(rootDirectory).map((d) => {
d = path.join(rootDirectory, d);
return {path: d, birth: fs.statSync(d).birthtimeMs};
});
directories.sort((a, b) => b.birth - a.birth);
directories = directories.slice(10, directories.length);
directories.forEach((dir) => {
logger.debug(`Removing old log directory '${dir.path}'`);
rimraf.sync(dir.path);
});
module.exports = logger;
+17 -3
View File
@@ -13,7 +13,7 @@ const shepherdSettings = {
},
dbPath: data.joinPath('database.db'),
sp: {
baudrate: advancedSettings && advancedSettings.baudrate ? advancedSettings.baudrate : 115200,
baudRate: advancedSettings && advancedSettings.baudrate ? advancedSettings.baudrate : 115200,
rtscts: advancedSettings && (typeof(advancedSettings.rtscts) === 'boolean') ? advancedSettings.rtscts : true,
},
};
@@ -68,6 +68,7 @@ class Zigbee {
_logStartupInfo() {
logger.info('zigbee-shepherd started');
logger.info(`Coordinator firmware version: '${this.shepherd.info().firmware.revision}'`);
logger.debug(`zigbee-shepherd info: ${JSON.stringify(this.shepherd.info())}`);
}
@@ -153,11 +154,16 @@ class Zigbee {
}
ping(deviceID) {
let friendlyName = 'unknown';
const device = this.shepherd._findDevByAddr(deviceID);
const ieeeAddr = device.ieeeAddr;
if (settings.getDevice(ieeeAddr)) {
friendlyName = settings.getDevice(ieeeAddr).friendly_name;
}
if (device) {
// Note: checkOnline has the callback argument but does not call callback
logger.debug(`Check online ${deviceID}`);
logger.debug(`Check online ${friendlyName} ${deviceID}`);
this.shepherd.controller.checkOnline(device);
}
}
@@ -185,7 +191,15 @@ class Zigbee {
}
logger.info(`Zigbee publish to '${deviceID}', ${cid} - ${cmd} - ${JSON.stringify(zclData)} - ${ep}`);
device.functional(cid, cmd, zclData, callback);
device.functional(cid, cmd, zclData, (error) => {
if (error) {
logger.error(
`Zigbee publish to '${deviceID}', ${cid} - ${cmd} - ${JSON.stringify(zclData)} - ${ep} ` +
`failed with error ${error}`);
}
callback(error);
});
}
read(deviceID, cid, attr, ep, callback) {
+4377 -1504
View File
File diff suppressed because it is too large Load Diff
+24 -13
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "0.1.2",
"version": "0.1.3",
"description": "Zigbee to MQTT bridge using zigbee-shepherd",
"main": "index.js",
"repository": {
@@ -17,10 +17,12 @@
"cc2531"
],
"scripts": {
"start": "node index.js",
"coverage": "nyc --all report --reporter=text mocha --recursive --timeout=3000",
"docgen": "node support/docgen.js",
"verify-homeassistant-mapping": "node support/verify-homeassistant-mapping.js",
"eslint": "node_modules/.bin/eslint ."
"eslint": "node_modules/.bin/eslint .",
"start": "node index.js",
"test": "mocha --recursive",
"verify-homeassistant-mapping": "node support/verify-homeassistant-mapping.js"
},
"author": "Koen Kanters",
"license": "GPL-3.0",
@@ -29,18 +31,27 @@
},
"homepage": "https://github.com/Koenkk/zigbee2mqtt/wiki",
"dependencies": {
"object-assign-deep": "*",
"mqtt": "*",
"winston": "2.4.2",
"zigbee-shepherd": "git+https://github.com/Koenkk/zigbee-shepherd.git#2078557a485466c032cc26b2cd2b8ac29edeceac",
"zigbee-shepherd-converters": "2.0.34",
"json2yaml": "*",
"zcl-packet": "git+https://github.com/Koenkk/zcl-packet.git#3ea8912f9d228b59fad554064d15ed9be7897a7f",
"git-last-commit": "*",
"js-yaml": "*",
"git-last-commit": "*"
"json2yaml": "*",
"mkdir-recursive": "*",
"moment": "*",
"mqtt": "*",
"object-assign-deep": "*",
"rimraf": "*",
"winston": "2.4.2",
"zcl-packet": "git+https://github.com/Koenkk/zcl-packet.git#b7d5b4478a88cd3fc65328c6dc94572c277c7d3e",
"zigbee-shepherd": "git+https://github.com/Koenkk/zigbee-shepherd.git#7673fc5a285dc93d2e466bc2aa7134220e1fb134",
"zigbee-shepherd-converters": "*"
},
"devDependencies": {
"chai": "*",
"chai-spies": "*",
"eslint": "*",
"eslint-config-google": "*"
"eslint-config-google": "*",
"eslint-plugin-mocha": "*",
"mocha": "*",
"nyc": "*",
"proxyquire": "*"
}
}
+23
View File
@@ -0,0 +1,23 @@
/* global describe, it */
const chai = require('chai');
const proxyquire = require('proxyquire').noPreserveCache();
const data = () => proxyquire('../lib/util/data.js', {});
const path = require('path');
describe('Data', () => {
describe('Get path', () => {
it('Should return correct path', () => {
const expected = path.normalize(path.join(__dirname, '..', 'data'));
const actual = data().getPath();
chai.assert.strictEqual(actual, expected);
});
it('Should return correct path when ZIGBEE2MQTT_DATA set', () => {
const expected = path.join('var', 'zigbee2mqtt');
process.env.ZIGBEE2MQTT_DATA = expected;
const actual = data().getPath();
chai.assert.strictEqual(actual, expected);
});
});
});