### 1.0.0 (2020-12-13)

* (bluefox) added the support of compact mode
This commit is contained in:
GermanBluefox
2020-12-13 17:50:27 +01:00
parent 09436c0929
commit d7adbddcc0
14 changed files with 658 additions and 407 deletions
+32
View File
@@ -0,0 +1,32 @@
---
name: Bug report
about: Something is not working as it should
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '...'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots & Logfiles**
If applicable, add screenshots and logfiles to help explain your problem.
**Versions:**
- Adapter version: <adapter-version>
- JS-Controller version: <js-controller-version> <!-- determine this with `iobroker -v` on the console -->
- Node version: <node-version> <!-- determine this with `node -v` on the console -->
- Operating system: <os-name>
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+78
View File
@@ -0,0 +1,78 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 90
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- enhancement
- security
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false
# Label to use when marking as stale
staleLabel: wontfix
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs within the next 7 days.
Please check if the issue is still relevant in the most current version of the adapter
and tell us. Also check that all relevant details, logs and reproduction steps
are included and update them if needed.
Thank you for your contributions.
Dieses Problem wurde automatisch als veraltet markiert, da es in letzter Zeit keine Aktivitäten gab.
Es wird geschlossen, wenn nicht innerhalb der nächsten 7 Tage weitere Aktivitäten stattfinden.
Bitte überprüft, ob das Problem auch in der aktuellsten Version des Adapters noch relevant ist,
und teilt uns dies mit. Überprüft auch, ob alle relevanten Details, Logs und Reproduktionsschritte
enthalten sind bzw. aktualisiert diese.
Vielen Dank für Eure Unterstützung.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been automatically closed because of inactivity. Please open a new
issue if still relevant and make sure to include all relevant details, logs and
reproduction steps.
Thank you for your contributions.
Dieses Problem wurde aufgrund von Inaktivität automatisch geschlossen. Bitte öffnet ein
neues Issue, falls dies noch relevant ist und stellt sicher das alle relevanten Details,
Logs und Reproduktionsschritte enthalten sind.
Vielen Dank für Eure Unterstützung.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
only: issues
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed
+159
View File
@@ -0,0 +1,159 @@
# This is a composition of lint and test scripts
# Make sure to update this file along with the others
name: Test and Release
# Run this job on all pushes and pull requests
# as well as tags with a semantic version
on:
push:
branches:
- '*'
tags:
# normal versions
- "v?[0-9]+.[0-9]+.[0-9]+"
# pre-releases
- "v?[0-9]+.[0-9]+.[0-9]+-**"
pull_request: {}
jobs:
# Performs quick checks before the expensive test runs
check-and-lint:
if: contains(github.event.head_commit.message, '[skip ci]') == false
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm install
# - name: Perform a type check
# run: npm run check:ts
# env:
# CI: true
# - name: Lint TypeScript code
# run: npm run lint
# - name: Test package files
# run: npm run test:package
# Runs adapter tests on all supported node versions and OSes
adapter-tests:
if: contains(github.event.head_commit.message, '[skip ci]') == false
needs: [check-and-lint]
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [10.x, 12.x, 14.x]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm install
- name: Run local tests
run: npm test
# - name: Run unit tests
# run: npm run test:unit
# - name: Run integration tests # (linux/osx)
# if: startsWith(runner.OS, 'windows') == false
# run: DEBUG=testing:* npm run test:integration
# - name: Run integration tests # (windows)
# if: startsWith(runner.OS, 'windows')
# run: set DEBUG=testing:* & npm run test:integration
# Deploys the final package to NPM
deploy:
needs: [adapter-tests]
# Trigger this step only when a commit on master is tagged with a version number
if: |
contains(github.event.head_commit.message, '[skip ci]') == false &&
github.event_name == 'push' &&
github.event.base_ref == 'refs/heads/master' &&
startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Extract the version and commit body from the tag
id: extract_release
# The body may be multiline, therefore we need to escape some characters
run: |
VERSION="${{ github.ref }}"
VERSION=${VERSION##*/}
VERSION=${VERSION##*v}
echo "::set-output name=VERSION::$VERSION"
BODY=$(git show -s --format=%b)
BODY="${BODY//'%'/'%25'}"
BODY="${BODY//$'\n'/'%0A'}"
BODY="${BODY//$'\r'/'%0D'}"
echo "::set-output name=BODY::$BODY"
- name: Install Dependencies
run: npm install
# - name: Create a clean build
# run: npm run build
- name: Publish package to npm
run: |
npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
npm whoami
npm publish
- name: Create Github Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release v${{ steps.extract_release.outputs.VERSION }}
draft: false
# Prerelease versions create prereleases on Github
prerelease: ${{ contains(steps.extract_release.outputs.VERSION, '-') }}
body: ${{ steps.extract_release.outputs.BODY }}
#- name: Notify Sentry.io about the release
# run: |
# npm i -g @sentry/cli
# export SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
# export SENTRY_URL=https://sentry.iobroker.net
# export SENTRY_ORG=iobroker
# export SENTRY_PROJECT=iobroker-hass
# export SENTRY_VERSION=iobroker.hass@${{ steps.extract_release.outputs.VERSION }}
# sentry-cli releases new $SENTRY_VERSION
# sentry-cli releases finalize $SENTRY_VERSION
# Add the following line BEFORE finalize if repositories are connected in Sentry
# sentry-cli releases set-commits $SENTRY_VERSION --auto
# Add the following line BEFORE finalize if sourcemap uploads are needed
# sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/
+11 -8
View File
@@ -1,9 +1,12 @@
/**/*
/gulpfile.js
gulpfile.js gulpfile.js
tasks !/admin/**/*
tmp !/admin/*
test !/lib/**/*
.travis.yml !/lib/*
appveyor.yml !/io-package.json
admin/i18n !/package.json
iob_npm.done !/LICENSE
package-lock.json !/main.js
!/README.md
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 bluefox <dogafox@gmail.com> Copyright (c) 2018-2020 bluefox <dogafox@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+20 -5
View File
@@ -1,23 +1,38 @@
![Logo](admin/hass.png) ![Logo](admin/hass.png)
# ioBroker.hass # ioBroker.hass
=================
[![NPM version](http://img.shields.io/npm/v/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass) [![NPM version](http://img.shields.io/npm/v/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass)
[![Downloads](https://img.shields.io/npm/dm/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass) [![Downloads](https://img.shields.io/npm/dm/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass)
[![NPM](https://nodei.co/npm/iobroker.hass.png?downloads=true)](https://nodei.co/npm/iobroker.hass/)
This adapter allows the connecting of Home Assistant to ioBroker.
This adapter allows connect Home Assistant to ioBroker. ## Usage
Create a long term token in HASS and use it as PW (copy it also in the repeat field).
## Configuration
There is a good article about connection.
Please check it https://www.smarthomejetzt.de/mit-iobroker-auf-eine-home-assistant-hass-io-installation-und-die-geraete-zugreifen/
**Unfortunately only in german, but the [google translate works rather good](https://translate.google.com/translate?hl=en&sl=de&tl=en&u=https%3A%2F%2Fwww.smarthomejetzt.de%2Fmit-iobroker-auf-eine-home-assistant-hass-io-installation-und-die-geraete-zugreifen%2F)**
<!--
Placeholder for the next version (at the beginning of the line):
### __WORK IN PROGRESS__
-->
## Changelog ## Changelog
#### 0.1.0 ### 1.0.0 (2020-12-13)
* (bluefox) added the support of compact mode
### 0.1.0
* (bluefox) initial release * (bluefox) initial release
## License ## License
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 bluefox <dogafox@gmail.com> Copyright (c) 2018-2020 bluefox <dogafox@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+1 -2
View File
@@ -56,12 +56,11 @@
callback(obj); callback(obj);
} }
</script> </script>
</head> </head>
<body> <body>
<div id="adapter-container"> <div id="adapter-container">
<table><tr> <table><tr>
<td><img src="hass.png"/></td> <td><img src="hass.png" alt="logo"/></td>
<td><h3 class="translate">Home assistant adapter settings</h3></td> <td><h3 class="translate">Home assistant adapter settings</h3></td>
</tr></table> </tr></table>
<table> <table>
+88 -86
View File
@@ -1,10 +1,10 @@
'use strict'; 'use strict';
var gulp = require('gulp'); const gulp = require('gulp');
var fs = require('fs'); const fs = require('fs');
var pkg = require('./package.json'); const pkg = require('./package.json');
var iopackage = require('./io-package.json'); const iopackage = require('./io-package.json');
var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version; const version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
/*var appName = getAppName(); /*var appName = getAppName();
function getAppName() { function getAppName() {
@@ -13,7 +13,7 @@ function getAppName() {
} }
*/ */
const fileName = 'words.js'; const fileName = 'words.js';
var languages = { let languages = {
en: {}, en: {},
de: {}, de: {},
ru: {}, ru: {},
@@ -22,24 +22,27 @@ var languages = {
fr: {}, fr: {},
it: {}, it: {},
es: {}, es: {},
pl: {} pl: {},
'zh-cn': {}
}; };
function lang2data(lang, isFlat) { function lang2data(lang, isFlat) {
var str = isFlat ? '' : '{\n'; let str = isFlat ? '' : '{\n';
var count = 0; let count = 0;
for (var w in lang) { for (const w in lang) {
if (lang.hasOwnProperty(w)) { if (lang.hasOwnProperty(w)) {
count++; count++;
if (isFlat) { if (isFlat) {
str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n'; str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n';
} else { } else {
var key = ' "' + w.replace(/"/g, '\\"') + '": '; const key = ' "' + w.replace(/"/g, '\\"') + '": ';
str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n'; str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
} }
} }
} }
if (!count) return isFlat ? '' : '{\n}'; if (!count) {
return isFlat ? '' : '{\n}';
}
if (isFlat) { if (isFlat) {
return str; return str;
} else { } else {
@@ -49,15 +52,15 @@ function lang2data(lang, isFlat) {
function readWordJs(src) { function readWordJs(src) {
try { try {
var words; let words;
if (fs.existsSync(src + 'js/' + fileName)) { if (fs.existsSync(src + 'js/' + fileName)) {
words = fs.readFileSync(src + 'js/' + fileName).toString(); words = fs.readFileSync(src + 'js/' + fileName).toString();
} else { } else {
words = fs.readFileSync(src + fileName).toString(); words = fs.readFileSync(src + fileName).toString();
} }
var lines = words.split(/\r\n|\r|\n/g); const lines = words.split(/\r\n|\r|\n/g);
var i = 0; let i = 0;
while (!lines[i].match(/^systemDictionary = {/)) { while (!lines[i].match(/^systemDictionary = {/)) {
i++; i++;
} }
@@ -75,7 +78,7 @@ function readWordJs(src) {
lines[0] = lines[0].replace('systemDictionary = ', ''); lines[0] = lines[0].replace('systemDictionary = ', '');
lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}'); lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}');
words = lines.join('\n'); words = lines.join('\n');
var resultFunc = new Function('return ' + words + ';'); const resultFunc = new Function('return ' + words + ';');
return resultFunc(); return resultFunc();
} catch (e) { } catch (e) {
@@ -86,16 +89,16 @@ function padRight(text, totalLength) {
return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : ''); return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : '');
} }
function writeWordJs(data, src) { function writeWordJs(data, src) {
var text = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n'; let text = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n';
text += '/*global systemDictionary:true */\n'; text += '/*global systemDictionary:true */\n';
text += '\'use strict\';\n\n'; text += '\'use strict\';\n\n';
text += 'systemDictionary = {\n'; text += 'systemDictionary = {\n';
for (var word in data) { for (const word in data) {
if (data.hasOwnProperty(word)) { if (data.hasOwnProperty(word)) {
text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50); text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50);
var line = ''; let line = '';
for (var lang in data[word]) { for (const lang in data[word]) {
if (data[word].hasOwnProperty(lang)) { if (data[word].hasOwnProperty(lang)) {
line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' '; line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' ';
} }
@@ -120,16 +123,16 @@ function writeWordJs(data, src) {
const EMPTY = ''; const EMPTY = '';
function words2languages(src) { function words2languages(src) {
var langs = Object.assign({}, languages); const langs = Object.assign({}, languages);
var data = readWordJs(src); const data = readWordJs(src);
if (data) { if (data) {
for (var word in data) { for (const word in data) {
if (data.hasOwnProperty(word)) { if (data.hasOwnProperty(word)) {
for (var lang in data[word]) { for (const lang in data[word]) {
if (data[word].hasOwnProperty(lang)) { if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang]; langs[lang][word] = data[word][lang];
// pre-fill all other languages // pre-fill all other languages
for (var j in langs) { for (const j in langs) {
if (langs.hasOwnProperty(j)) { if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY; langs[j][word] = langs[j][word] || EMPTY;
} }
@@ -141,12 +144,12 @@ function words2languages(src) {
if (!fs.existsSync(src + 'i18n/')) { if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/'); fs.mkdirSync(src + 'i18n/');
} }
for (var l in langs) { for (const l in langs) {
if (!langs.hasOwnProperty(l)) continue; if (!langs.hasOwnProperty(l)) continue;
var keys = Object.keys(langs[l]); const keys = Object.keys(langs[l]);
//keys.sort(); //keys.sort();
var obj = {}; let obj = {};
for (var k = 0; k < keys.length; k++) { for (let k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]]; obj[keys[k]] = langs[l][keys[k]];
} }
if (!fs.existsSync(src + 'i18n/' + l)) { if (!fs.existsSync(src + 'i18n/' + l)) {
@@ -160,16 +163,16 @@ function words2languages(src) {
} }
} }
function words2languagesFlat(src) { function words2languagesFlat(src) {
var langs = Object.assign({}, languages); let langs = Object.assign({}, languages);
var data = readWordJs(src); const data = readWordJs(src);
if (data) { if (data) {
for (var word in data) { for (const word in data) {
if (data.hasOwnProperty(word)) { if (data.hasOwnProperty(word)) {
for (var lang in data[word]) { for (const lang in data[word]) {
if (data[word].hasOwnProperty(lang)) { if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang]; langs[lang][word] = data[word][lang];
// pre-fill all other languages // pre-fill all other languages
for (var j in langs) { for (const j in langs) {
if (langs.hasOwnProperty(j)) { if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY; langs[j][word] = langs[j][word] || EMPTY;
} }
@@ -178,12 +181,12 @@ function words2languagesFlat(src) {
} }
} }
} }
var keys = Object.keys(langs.en); const keys = Object.keys(langs.en);
//keys.sort(); //keys.sort();
for (var l in langs) { for (const l in langs) {
if (!langs.hasOwnProperty(l)) continue; if (!langs.hasOwnProperty(l)) continue;
var obj = {}; let obj = {};
for (var k = 0; k < keys.length; k++) { for (let k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]]; obj[keys[k]] = langs[l][keys[k]];
} }
langs[l] = obj; langs[l] = obj;
@@ -191,7 +194,7 @@ function words2languagesFlat(src) {
if (!fs.existsSync(src + 'i18n/')) { if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/'); fs.mkdirSync(src + 'i18n/');
} }
for (var ll in langs) { for (const ll in langs) {
if (!langs.hasOwnProperty(ll)) continue; if (!langs.hasOwnProperty(ll)) continue;
if (!fs.existsSync(src + 'i18n/' + ll)) { if (!fs.existsSync(src + 'i18n/' + ll)) {
fs.mkdirSync(src + 'i18n/' + ll); fs.mkdirSync(src + 'i18n/' + ll);
@@ -205,13 +208,13 @@ function words2languagesFlat(src) {
} }
} }
function languagesFlat2words(src) { function languagesFlat2words(src) {
var dirs = fs.readdirSync(src + 'i18n/'); const dirs = fs.readdirSync(src + 'i18n/');
var langs = {}; let langs = {};
var bigOne = {}; let bigOne = {};
var order = Object.keys(languages); const order = Object.keys(languages);
dirs.sort(function (a, b) { dirs.sort((a, b) => {
var posA = order.indexOf(a); const posA = order.indexOf(a);
var posB = order.indexOf(b); const posB = order.indexOf(b);
if (posA === -1 && posB === -1) { if (posA === -1 && posB === -1) {
if (a > b) return 1; if (a > b) return 1;
if (a < b) return -1; if (a < b) return -1;
@@ -226,19 +229,19 @@ function languagesFlat2words(src) {
return 0; return 0;
} }
}); });
var keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n'); const keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n');
for (var l = 0; l < dirs.length; l++) { for (let l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue; if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l]; let lang = dirs[l];
var values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n'); const values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n');
langs[lang] = {}; langs[lang] = {};
keys.forEach(function (word, i) { keys.forEach(function (word, i) {
langs[lang][word] = values[i].replace(/<\/ i>/g, '</i>').replace(/<\/ b>/g, '</b>').replace(/<\/ span>/g, '</span>').replace(/% s/g, ' %s'); langs[lang][word] = values[i].replace(/<\/ i>/g, '</i>').replace(/<\/ b>/g, '</b>').replace(/<\/ span>/g, '</span>').replace(/% s/g, ' %s');
}); });
var words = langs[lang]; const words = langs[lang];
for (var word in words) { for (const word in words) {
if (words.hasOwnProperty(word)) { if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {}; bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) { if (words[word] !== EMPTY) {
@@ -248,12 +251,12 @@ function languagesFlat2words(src) {
} }
} }
// read actual words.js // read actual words.js
var aWords = readWordJs(); const aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt']; const temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt'];
if (aWords) { if (aWords) {
// Merge words together // Merge words together
for (var w in aWords) { for (const w in aWords) {
if (aWords.hasOwnProperty(w)) { if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) { if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w); console.warn('Take from actual words.js: ' + w);
@@ -273,13 +276,13 @@ function languagesFlat2words(src) {
writeWordJs(bigOne, src); writeWordJs(bigOne, src);
} }
function languages2words(src) { function languages2words(src) {
var dirs = fs.readdirSync(src + 'i18n/'); const dirs = fs.readdirSync(src + 'i18n/');
var langs = {}; let langs = {};
var bigOne = {}; let bigOne = {};
var order = Object.keys(languages); const order = Object.keys(languages);
dirs.sort(function (a, b) { dirs.sort((a, b) => {
var posA = order.indexOf(a); const posA = order.indexOf(a);
var posB = order.indexOf(b); const posB = order.indexOf(b);
if (posA === -1 && posB === -1) { if (posA === -1 && posB === -1) {
if (a > b) return 1; if (a > b) return 1;
if (a < b) return -1; if (a < b) return -1;
@@ -294,13 +297,13 @@ function languages2words(src) {
return 0; return 0;
} }
}); });
for (var l = 0; l < dirs.length; l++) { for (let l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue; if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l]; const lang = dirs[l];
langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString(); langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString();
langs[lang] = JSON.parse(langs[lang]); langs[lang] = JSON.parse(langs[lang]);
var words = langs[lang]; const words = langs[lang];
for (var word in words) { for (const word in words) {
if (words.hasOwnProperty(word)) { if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {}; bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) { if (words[word] !== EMPTY) {
@@ -310,12 +313,12 @@ function languages2words(src) {
} }
} }
// read actual words.js // read actual words.js
var aWords = readWordJs(); const aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'it']; const temporaryIgnore = ['pt', 'fr', 'nl', 'it'];
if (aWords) { if (aWords) {
// Merge words together // Merge words together
for (var w in aWords) { for (const w in aWords) {
if (aWords.hasOwnProperty(w)) { if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) { if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w); console.warn('Take from actual words.js: ' + w);
@@ -335,33 +338,32 @@ function languages2words(src) {
writeWordJs(bigOne, src); writeWordJs(bigOne, src);
} }
gulp.task('adminWords2languages', function (done) { gulp.task('admin (words.js => json)', done => {
words2languages('./admin/'); words2languages('./admin/');
done(); done();
}); });
gulp.task('adminWords2languagesFlat', function (done) { gulp.task('admin (words.js => flat)', done => {
words2languagesFlat('./admin/'); words2languagesFlat('./admin/');
done(); done();
}); });
gulp.task('adminLanguagesFlat2words', function (done) { gulp.task('admin (flat => words.js)', done => {
languagesFlat2words('./admin/'); languagesFlat2words('./admin/');
done(); done();
}); });
gulp.task('adminLanguages2words', function (done) { gulp.task('admin (json => words.js)', done => {
languages2words('./admin/'); languages2words('./admin/');
done(); done();
}); });
gulp.task('updatePackages', done => {
gulp.task('updatePackages', function (done) {
iopackage.common.version = pkg.version; iopackage.common.version = pkg.version;
iopackage.common.news = iopackage.common.news || {}; iopackage.common.news = iopackage.common.news || {};
if (!iopackage.common.news[pkg.version]) { if (!iopackage.common.news[pkg.version]) {
var news = iopackage.common.news; const news = iopackage.common.news;
var newNews = {}; let newNews = {};
newNews[pkg.version] = { newNews[pkg.version] = {
en: 'news', en: 'news',
@@ -374,20 +376,20 @@ gulp.task('updatePackages', function (done) {
done(); done();
}); });
gulp.task('updateReadme', function (done) { gulp.task('updateReadme', done => {
var readme = fs.readFileSync('README.md').toString(); const readme = fs.readFileSync('README.md').toString();
var pos = readme.indexOf('## Changelog\n'); const pos = readme.indexOf('## Changelog\n');
if (pos !== -1) { if (pos !== -1) {
var readmeStart = readme.substring(0, pos + '## Changelog\n'.length); let readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
var readmeEnd = readme.substring(pos + '## Changelog\n'.length); let readmeEnd = readme.substring(pos + '## Changelog\n'.length);
if (readme.indexOf(version) === -1) { if (readme.indexOf(version) === -1) {
var timestamp = new Date(); const timestamp = new Date();
var date = timestamp.getFullYear() + '-' + const date = timestamp.getFullYear() + '-' +
('0' + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' + ('0' + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' +
('0' + (timestamp.getDate()).toString(10)).slice(-2); ('0' + (timestamp.getDate()).toString(10)).slice(-2);
var news = ''; let news = '';
if (iopackage.common.news && iopackage.common.news[pkg.version]) { if (iopackage.common.news && iopackage.common.news[pkg.version]) {
news += '* ' + iopackage.common.news[pkg.version].en; news += '* ' + iopackage.common.news[pkg.version].en;
} }
@@ -398,4 +400,4 @@ gulp.task('updateReadme', function (done) {
done(); done();
}); });
gulp.task('default', ['updatePackages', 'updateReadme']); gulp.task('default', gulp.series('updatePackages', 'updateReadme'));
+74 -56
View File
@@ -1,58 +1,76 @@
{ {
"common": { "common": {
"name": "hass", "name": "hass",
"version": "0.1.0", "version": "1.0.0",
"title": "Home Assistant", "title": "Home Assistant",
"desc": { "desc": {
"en": "Home Assistant connection for ioBroker", "en": "Home Assistant connection for ioBroker",
"de": "Home Assistant Verbindung für ioBroker", "de": "Home Assistant Verbindung für ioBroker",
"ru": "Подключение Home Assistant для ioBroker", "ru": "Подключение Home Assistant для ioBroker",
"pt": "Conexão do Home Assistant para ioBroker", "pt": "Conexão do Home Assistant para ioBroker",
"nl": "Home Assistant-verbinding voor ioBroker", "nl": "Home Assistant-verbinding voor ioBroker",
"fr": "Connexion Home Assistant pour ioBroker", "fr": "Connexion Home Assistant pour ioBroker",
"it": "Connessione Home Assistant per ioBroker", "it": "Connessione Home Assistant per ioBroker",
"es": "Conexión de Home Assistant para ioBroker", "es": "Conexión de Home Assistant para ioBroker",
"pl": "Połączenie Home Assistant dla ioBroker" "pl": "Połączenie Home Assistant dla ioBroker"
},
"news": {
"0.1.0" : {
"en": "initial version",
"de": "erste Version",
"ru": "Первоначальная версия",
"pt": "versão inicial",
"nl": "eerste versie",
"fr": "version initiale",
"it": "versione iniziale",
"es": "versión inicial",
"pl": "początkowa wersja"
}
},
"localLink": "http://%host%:8123",
"platform": "Javascript/Node.js",
"mode": "daemon",
"icon": "hass.png",
"enabled": true,
"extIcon": "https://raw.githubusercontent.com/ioBroker/ioBroker.hass/master/admin/hass.png",
"keywords": ["HASS", "Home", "assistant"],
"readme": "https://github.com/ioBroker/ioBroker.hass/blob/master/README.md",
"loglevel": "info",
"type": "iot-systems",
"authors": [
"bluefox <dogafox@gmail.com>"
]
}, },
"native": { "news": {
"host": "127.0.0.1", "1.0.0": {
"port": 8123, "en": "added the support of compact mode",
"password": "", "de": "fügte die Unterstützung des Kompaktmodus hinzu",
"secure": false "ru": "добавлена поддержка компактного режима",
"pt": "adicionado o suporte do modo compacto",
"nl": "de ondersteuning van de compacte modus toegevoegd",
"fr": "ajouté le support du mode compact",
"it": "aggiunto il supporto della modalità compatta",
"es": "agregó el soporte del modo compacto",
"pl": "dodano obsługę trybu kompaktowego",
"zh-cn": "增加了对紧凑模式的支持"
},
"0.1.0": {
"en": "initial version",
"de": "erste Version",
"ru": "Первоначальная версия",
"pt": "versão inicial",
"nl": "eerste versie",
"fr": "version initiale",
"it": "versione iniziale",
"es": "versión inicial",
"pl": "początkowa wersja"
}
}, },
"objects": [ "localLink": "http://%host%:8123",
"platform": "Javascript/Node.js",
"mode": "daemon",
"icon": "hass.png",
"enabled": true,
"compact": true,
"materialize": true,
"license": "MIT",
"extIcon": "https://raw.githubusercontent.com/ioBroker/ioBroker.hass/master/admin/hass.png",
"keywords": [
"HASS",
"Home",
"assistant"
], ],
"readme": "https://github.com/ioBroker/ioBroker.hass/blob/master/README.md",
"loglevel": "info",
"type": "iot-systems",
"authors": [
"bluefox <dogafox@gmail.com>"
]
},
"native": {
"host": "127.0.0.1",
"port": 8123,
"password": "",
"secure": false
},
"objects": [
],
"instanceObjects": [ "instanceObjects": [
{ {
"_id": "info", "_id": "info",
"type": "channel", "type": "channel",
"common": { "common": {
"name": "Information" "name": "Information"
@@ -60,20 +78,20 @@
"native": {} "native": {}
}, },
{ {
"_id": "info.connection", "_id": "info.connection",
"type": "state", "type": "state",
"common": { "common": {
"role": "indicator.connected", "role": "indicator.connected",
"name": "If connected to HASS server", "name": "If connected to HASS server",
"type": "boolean", "type": "boolean",
"read": true, "read": true,
"write": false, "write": false,
"def": false "def": false
}, },
"native": {} "native": {}
}, },
{ {
"_id": "entities", "_id": "entities",
"type": "channel", "type": "channel",
"common": { "common": {
"name": "HASS entities" "name": "HASS entities"
+37 -50
View File
@@ -1,30 +1,32 @@
var util = require('util'); const util = require('util');
var EventEmitter = require('events').EventEmitter; const EventEmitter = require('events').EventEmitter;
var WebSocket = require('ws'); const WebSocket = require('ws');
function HASS(options, log) { function HASS(options, log) {
if (!(this instanceof HASS)) return new HASS(options); if (!(this instanceof HASS)) {
return new HASS(options);
}
options = options || {}; options = options || {};
options.host = options.host || '127.0.0.1'; options.host = options.host || '127.0.0.1';
options.port = parseInt(options.port, 10) || 8123; options.port = parseInt(options.port, 10) || 8123;
var ERRORS = { const ERRORS = {
1: 'ERR_CANNOT_CONNECT', 1: 'ERR_CANNOT_CONNECT',
2: 'ERR_INVALID_AUTH', 2: 'ERR_INVALID_AUTH',
3: 'ERR_CONNECTION_LOST' 3: 'ERR_CONNECTION_LOST'
}; };
this.socket = null; this.socket = null;
var that = this; const that = this;
var currentId = 1; let currentId = 1;
var requests = {}; const requests = {};
var connected; let connected;
var connectTimeout = null; let connectTimeout = null;
function subscribeEvents(socket, callback) { function subscribeEvents(socket, callback) {
if (socket && typeof socket.send === 'function') { if (socket && typeof socket.send === 'function') {
var id = currentId++; const id = currentId++;
requests[id] = {type: 'subscribe_events', ts: Date.now(), cb: callback}; requests[id] = {type: 'subscribe_events', ts: Date.now(), cb: callback};
socket.send(JSON.stringify({ socket.send(JSON.stringify({
@@ -39,7 +41,7 @@ function HASS(options, log) {
function getConfig(socket, callback) { function getConfig(socket, callback) {
if (socket && typeof socket.send === 'function') { if (socket && typeof socket.send === 'function') {
var id = currentId++; const id = currentId++;
requests[id] = {type: 'get_config', cb: callback, ts: Date.now()}; requests[id] = {type: 'get_config', cb: callback, ts: Date.now()};
socket.send(JSON.stringify({ socket.send(JSON.stringify({
id: id, id: id,
@@ -52,7 +54,7 @@ function HASS(options, log) {
function getStates(socket, callback) { function getStates(socket, callback) {
if (socket && typeof socket.send === 'function') { if (socket && typeof socket.send === 'function') {
var id = currentId++; const id = currentId++;
requests[id] = {type: 'get_states', cb: callback, ts: Date.now()}; requests[id] = {type: 'get_states', cb: callback, ts: Date.now()};
socket.send(JSON.stringify({ socket.send(JSON.stringify({
id: id, id: id,
@@ -65,7 +67,7 @@ function HASS(options, log) {
function getPanels(socket, callback) { function getPanels(socket, callback) {
if (socket && typeof socket.send === 'function') { if (socket && typeof socket.send === 'function') {
var id = currentId++; const id = currentId++;
requests[id] = {type: 'get_panels', cb: callback, ts: Date.now()}; requests[id] = {type: 'get_panels', cb: callback, ts: Date.now()};
socket.send(JSON.stringify({ socket.send(JSON.stringify({
id: id, id: id,
@@ -78,7 +80,7 @@ function HASS(options, log) {
function getServices(socket, callback) { function getServices(socket, callback) {
if (socket && typeof socket.send === 'function') { if (socket && typeof socket.send === 'function') {
var id = currentId++; const id = currentId++;
requests[id] = {type: 'get_services', cb: callback, ts: Date.now()}; requests[id] = {type: 'get_services', cb: callback, ts: Date.now()};
socket.send(JSON.stringify({ socket.send(JSON.stringify({
id: id, id: id,
@@ -91,7 +93,7 @@ function HASS(options, log) {
function callService(socket, service, domain, serviceData, callback) { function callService(socket, service, domain, serviceData, callback) {
if (socket && typeof socket.send === 'function') { if (socket && typeof socket.send === 'function') {
var id = currentId++; const id = currentId++;
requests[id] = {type: 'call_service', cb: callback, ts: Date.now()}; requests[id] = {type: 'call_service', cb: callback, ts: Date.now()};
socket.send(JSON.stringify({ socket.send(JSON.stringify({
id: id, id: id,
@@ -115,10 +117,10 @@ function HASS(options, log) {
} }
function initSocket(socket) { function initSocket(socket) {
socket.on('message', function (msg) { socket.on('message', msg => {
log.silly(msg); log.silly(msg);
var response = JSON.parse(msg); const response = JSON.parse(msg);
if (response.type === 'event') { if (response.type === 'event') {
if (response.event.data && response.event.event_type === 'system_log_event') { if (response.event.data && response.event.event_type === 'system_log_event') {
if (response.event.data.level === 'WARNING') { if (response.event.data.level === 'WARNING') {
@@ -138,25 +140,22 @@ function HASS(options, log) {
that.emit('error', 'Password required. Connection closed'); that.emit('error', 'Password required. Connection closed');
socket.terminate(); socket.terminate();
} else { } else {
setTimeout(function () { setTimeout(() => sendAuth(socket, options.password), 50);
sendAuth(socket, options.password);
}, 50);
} }
} else } else
if (response.type === 'auth_ok') { if (response.type === 'auth_ok') {
setImmediate(function () { setImmediate(() =>
subscribeEvents(socket, function (err) { subscribeEvents(socket, err => {
if (!err) { if (!err) {
connected = true; connected = true;
that.emit('connected'); that.emit('connected');
} }
}); }));
});
} else if (response.id === undefined) { } else if (response.id === undefined) {
log.error('Invalid answer: ' + msg); log.error(`Invalid answer: ${msg}`);
} else { } else {
if (response.type === 'result' && requests[response.id]) { if (response.type === 'result' && requests[response.id]) {
log.debug('got answer for ' + requests[response.id].type); log.debug(`got answer for ${requests[response.id].type}`);
if (typeof requests[response.id].cb === 'function') { if (typeof requests[response.id].cb === 'function') {
requests[response.id].cb(!response.success, response.result); requests[response.id].cb(!response.success, response.result);
delete requests[response.id]; delete requests[response.id];
@@ -165,7 +164,7 @@ function HASS(options, log) {
} }
}); });
socket.on('error', function (err) { socket.on('error', err => {
socket = null; socket = null;
if (err && err.message.indexOf('RSV2 and RSV3 must be clear') !== -1) { if (err && err.message.indexOf('RSV2 and RSV3 must be clear') !== -1) {
// ignore deflate error // ignore deflate error
@@ -173,12 +172,12 @@ function HASS(options, log) {
log.error(err); log.error(err);
} }
}); });
socket.on('open', function () { socket.on('open', () => {
if (!connected) { if (!connected) {
} }
}); });
socket.on('close', function () { socket.on('close', () => {
that.socket = null; that.socket = null;
if (connected) { if (connected) {
connected = false; connected = false;
@@ -186,7 +185,7 @@ function HASS(options, log) {
} }
if (!connectTimeout) { if (!connectTimeout) {
setTimeout(function () { setTimeout(() => {
connectTimeout = null; connectTimeout = null;
that.connect(); that.connect();
}, 3000); }, 3000);
@@ -194,15 +193,11 @@ function HASS(options, log) {
}); });
} }
this.isConnected = function () { this.isConnected = () => connected;
return connected;
};
this.getConfig = function (callback) { this.getConfig = function (callback) {
if (!connected) { if (!connected) {
if (typeof callback === 'function') { typeof callback === 'function' && callback('not connected');
callback('not connected');
}
} else { } else {
getConfig(this.socket, callback); getConfig(this.socket, callback);
} }
@@ -210,9 +205,7 @@ function HASS(options, log) {
this.getStates = function (callback) { this.getStates = function (callback) {
if (!connected) { if (!connected) {
if (typeof callback === 'function') { typeof callback === 'function' && callback('not connected');
callback('not connected');
}
} else { } else {
getStates(this.socket, callback); getStates(this.socket, callback);
} }
@@ -220,9 +213,7 @@ function HASS(options, log) {
this.getServices = function (callback) { this.getServices = function (callback) {
if (!connected) { if (!connected) {
if (typeof callback === 'function') { typeof callback === 'function' && callback('not connected');
callback('not connected');
}
} else { } else {
getServices(this.socket, callback); getServices(this.socket, callback);
} }
@@ -230,9 +221,7 @@ function HASS(options, log) {
this.getPanels = function (callback) { this.getPanels = function (callback) {
if (!connected) { if (!connected) {
if (typeof callback === 'function') { typeof callback === 'function' && callback('not connected');
callback('not connected');
}
} else { } else {
getPanels(this.socket, callback); getPanels(this.socket, callback);
} }
@@ -240,9 +229,7 @@ function HASS(options, log) {
this.callService = function (service, domain, serviceData, callback) { this.callService = function (service, domain, serviceData, callback) {
if (!connected) { if (!connected) {
if (typeof callback === 'function') { typeof callback === 'function' && callback('not connected');
callback('not connected');
}
} else { } else {
callService(this.socket, service, domain, serviceData, callback); callService(this.socket, service, domain, serviceData, callback);
} }
@@ -254,7 +241,7 @@ function HASS(options, log) {
connectTimeout = null; connectTimeout = null;
} }
this.socket = new WebSocket('ws' + (options.secure ? 's' : '') + '://' + options.host + ':' + options.port + '/api/websocket', { this.socket = new WebSocket(`ws${options.secure ? 's' : ''}://${options.host}:${options.port}/api/websocket`, {
perMessageDeflate: false perMessageDeflate: false
}); });
-64
View File
@@ -1,64 +0,0 @@
var controllerDir;
var appName;
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 2].split('.')[0];
}
// Get js-controller directory to load libs
function getControllerDir(isInstall) {
var fs = require('fs');
// Find the js-controller location
var controllerDir = __dirname.replace(/\\/g, '/');
controllerDir = controllerDir.split('/');
if (controllerDir[controllerDir.length - 3] === 'adapter') {
controllerDir.splice(controllerDir.length - 3, 3);
controllerDir = controllerDir.join('/');
} else if (controllerDir[controllerDir.length - 3] === 'node_modules') {
controllerDir.splice(controllerDir.length - 3, 3);
controllerDir = controllerDir.join('/');
if (fs.existsSync(controllerDir + '/node_modules/' + appName + '.js-controller')) {
controllerDir += '/node_modules/' + appName + '.js-controller';
} else if (fs.existsSync(controllerDir + '/node_modules/' + appName.toLowerCase() + '.js-controller')) {
controllerDir += '/node_modules/' + appName.toLowerCase() + '.js-controller';
} else if (!fs.existsSync(controllerDir + '/controller.js')) {
if (!isInstall) {
console.log('Cannot find js-controller');
process.exit(10);
} else {
process.exit();
}
}
} else if (fs.existsSync(__dirname + '/../../node_modules/' + appName.toLowerCase() + '.js-controller')) {
controllerDir.splice(controllerDir.length - 2, 2);
return controllerDir.join('/') + '/node_modules/' + appName.toLowerCase() + '.js-controller';
} else {
if (!isInstall) {
console.log('Cannot find js-controller');
process.exit(10);
} else {
process.exit();
}
}
return controllerDir;
}
// Read controller configuration file
function getConfig() {
var fs = require('fs');
if (fs.existsSync(controllerDir + '/conf/' + appName + '.json')) {
return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName + '.json'));
} else if (fs.existsSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json')) {
return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json'));
} else {
throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
}
}
appName = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
exports.controllerDir = controllerDir;
exports.getConfig = getConfig;
exports.Adapter = require(controllerDir + '/lib/adapter.js');
exports.appName = appName;
+127 -128
View File
@@ -1,73 +1,66 @@
/* jshint -W097 */ /* jshint -W097 */
/* jshint strict:false */ /* jshint strict: false */
/* jslint node: true */ /* jslint node: true */
'use strict'; 'use strict';
var utils = require(__dirname + '/lib/utils'); const utils = require('@iobroker/adapter-core');
var HASS = require(__dirname + '/lib/hass'); const HASS = require('./lib/hass');
const adapterName = require('./package.json').name.split('.').pop();
var adapter = utils.Adapter('hass'); let connected = false;
let hass;
let adapter;
const hassObjects = {};
var connected = false; function startAdapter(options) {
var hass; options = options || {};
var hassObjects = {}; Object.assign(options, {name: adapterName, unload: stop});
adapter = new utils.Adapter(options);
// is called if a subscribed state changes // is called if a subscribed state changes
adapter.on('stateChange', function (id, state) { adapter.on('stateChange', (id, state) => {
// you can use the ack flag to detect if it is status (true) or command (false) // you can use the ack flag to detect if it is status (true) or command (false)
if (state && !state.ack) { if (state && !state.ack) {
if (!connected) { if (!connected) {
adapter.log.warn('Cannot send command to "' + id + '", because not connected'); return adapter.log.warn('Cannot send command to "' + id + '", because not connected');
return; }
} /*if (id === adapter.namespace + '.' + '.info.resync') {
/*if (id === adapter.namespace + '.' + '.info.resync') { queue.push({command: 'resync'});
queue.push({command: 'resync'}); processQueue();
processQueue(); } else */
} else */ if (hassObjects[id]) {
if (hassObjects[id]) { if (!hassObjects[id].common.write) {
if (!hassObjects[id].common.write) { adapter.log.warn('Object ' + id + ' is not writable!');
adapter.log.warn('Object ' + id + ' is not writable!'); } else {
} else { const serviceData = {};
var serviceData = {}; const fields = hassObjects[id].native.fields;
var fields = hassObjects[id].native.fields;
for (var field in fields) { for (const field in fields) {
if (!fields.hasOwnProperty(field)) continue; if (!fields.hasOwnProperty(field)) {
continue;
}
if (field === 'entity_id') { if (field === 'entity_id') {
serviceData.entity_id = hassObjects[id].native.entity_id serviceData.entity_id = hassObjects[id].native.entity_id
} else { } else {
serviceData[field] = state.val; serviceData[field] = state.val;
}
} }
hass.callService(hassObjects[id].native.attr, hassObjects[id].native.domain || hassObjects[id].native.type, serviceData, err =>
err && adapter.log.error('Cannot control ' + id + ': ' + err));
} }
hass.callService(hassObjects[id].native.attr, hassObjects[id].native.domain || hassObjects[id].native.type, serviceData, function (err) {
if (err) {
adapter.log.error('Cannot control ' + id + ': ' + err);
}
});
} }
} }
} });
});
// Some message was sent to adapter instance over message box. Used by email, pushover, text2speech, ... // is called when databases are connected and adapter received configuration.
adapter.on('message', function (obj) { // start here!
if (typeof obj === 'object' && obj.message) { adapter.on('ready', main);
if (obj.command === 'send') {
// e.g. send email or pushover or whatever
console.log('send command');
// Send response in callback if required return adapter;
if (obj.callback) adapter.sendTo(obj.from, obj.command, 'Message received', obj.callback); }
}
}
});
// is called when databases are connected and adapter received configuration.
// start here!
adapter.on('ready', main);
function getUnit(name) { function getUnit(name) {
name = name.toLowerCase(); name = name.toLowerCase();
@@ -87,37 +80,34 @@ function getUnit(name) {
function syncStates(states, cb) { function syncStates(states, cb) {
if (!states || !states.length) { if (!states || !states.length) {
cb(); return cb();
return;
} }
var state = states.shift(); const state = states.shift();
var id = state.id; const id = state.id;
delete state.id; delete state.id;
adapter.setForeignState(id, state, function (err) { adapter.setForeignState(id, state, err => {
if (err) adapter.log.error(err); err && adapter.log.error(err);
setImmediate(syncStates, states, cb); setImmediate(syncStates, states, cb);
}); });
} }
function syncObjects(objects, cb) { function syncObjects(objects, cb) {
if (!objects || !objects.length) { if (!objects || !objects.length) {
cb(); return cb();
return;
} }
var obj = objects.shift(); const obj = objects.shift();
hassObjects[obj._id] = obj; hassObjects[obj._id] = obj;
adapter.getForeignObject(obj._id, function (err, oldObj) { adapter.getForeignObject(obj._id, (err, oldObj) => {
if (err) adapter.log.error(err); err && adapter.log.error(err);
if (!oldObj) { if (!oldObj) {
adapter.log.debug('Create "' + obj._id + '"'); adapter.log.debug('Create "' + obj._id + '"');
hassObjects[obj._id] = obj; hassObjects[obj._id] = obj;
adapter.setForeignObject(obj._id, obj, function (err) { adapter.setForeignObject(obj._id, obj, err => {
if (err) adapter.log.error(err); err && adapter.log.error(err);
setImmediate(syncObjects, objects, cb); setImmediate(syncObjects, objects, cb);
}); });
} else { } else {
@@ -126,8 +116,8 @@ function syncObjects(objects, cb) {
oldObj.native = obj.native; oldObj.native = obj.native;
adapter.log.debug('Update "' + obj._id + '"'); adapter.log.debug('Update "' + obj._id + '"');
adapter.setForeignObject(obj._id, oldObj, function (err) { adapter.setForeignObject(obj._id, oldObj, err => {
if (err) adapter.log.error(err); err => adapter.log.error(err);
setImmediate(syncObjects, objects, cb); setImmediate(syncObjects, objects, cb);
}); });
} else { } else {
@@ -138,7 +128,7 @@ function syncObjects(objects, cb) {
} }
function syncRoom(room, members, cb) { function syncRoom(room, members, cb) {
adapter.getForeignObject('enum.rooms.' + room, function (err, obj) { adapter.getForeignObject('enum.rooms.' + room, (err, obj) => {
if (!obj) { if (!obj) {
obj = { obj = {
_id: 'enum.rooms.' + room, _id: 'enum.rooms.' + room,
@@ -150,15 +140,15 @@ function syncRoom(room, members, cb) {
native: {} native: {}
}; };
adapter.log.debug('Update "' + obj._id + '"'); adapter.log.debug('Update "' + obj._id + '"');
adapter.setForeignObject(obj._id, obj, function (err) { adapter.setForeignObject(obj._id, obj, err => {
if (err) adapter.log.error(err); err && adapter.log.error(err);
cb(); cb();
}); });
} else { } else {
obj.common = obj.common || {}; obj.common = obj.common || {};
obj.common.members = obj.common.members || []; obj.common.members = obj.common.members || [];
var changed = false; let changed = false;
for (var m = 0; m < members.length; m++) { for (let m = 0; m < members.length; m++) {
if (obj.common.members.indexOf(members[m]) === -1) { if (obj.common.members.indexOf(members[m]) === -1) {
changed = true; changed = true;
obj.common.members.push(members[m]); obj.common.members.push(members[m]);
@@ -166,8 +156,8 @@ function syncRoom(room, members, cb) {
} }
if (changed) { if (changed) {
adapter.log.debug('Update "' + obj._id + '"'); adapter.log.debug('Update "' + obj._id + '"');
adapter.setForeignObject(obj._id, obj, function (err) { adapter.setForeignObject(obj._id, obj, err => {
if (err) adapter.log.error(err); err && adapter.log.error(err);
cb(); cb();
}); });
} else { } else {
@@ -177,41 +167,41 @@ function syncRoom(room, members, cb) {
}); });
} }
var knownAttributes = { const knownAttributes = {
azimuth: {write: false, read: true, unit: '°'}, azimuth: {write: false, read: true, unit: '°'},
elevation: {write: false, read: true, unit: '°'} elevation: {write: false, read: true, unit: '°'}
}; };
var ERRORS = { const ERRORS = {
1: 'ERR_CANNOT_CONNECT', 1: 'ERR_CANNOT_CONNECT',
2: 'ERR_INVALID_AUTH', 2: 'ERR_INVALID_AUTH',
3: 'ERR_CONNECTION_LOST' 3: 'ERR_CONNECTION_LOST'
}; };
var mapTypes = { const mapTypes = {
'string': 'string', 'string': 'string',
'number': 'number', 'number': 'number',
'object': 'mixed', 'object': 'mixed',
'boolean': 'boolean' 'boolean': 'boolean'
}; };
var skipServices = [ const skipServices = [
'persistent_notification' 'persistent_notification'
]; ];
function parseStates(entities, services, callback) { function parseStates(entities, services, callback) {
var objs = []; const objs = [];
var states = []; const states = [];
var obj; let obj;
var channel; let channel;
for (var e = 0; e < entities.length; e++) { for (let e = 0; e < entities.length; e++) {
var entity = entities[e]; const entity = entities[e];
if (!entity) continue; if (!entity) continue;
var name = entity.name || (entity.attributes && entity.attributes.friendly_name ? entity.attributes.friendly_name : entity.entity_id); const name = entity.name || (entity.attributes && entity.attributes.friendly_name ? entity.attributes.friendly_name : entity.entity_id);
var desc = entity.attributes && entity.attributes.attribution ? entity.attributes.attribution : undefined; const desc = entity.attributes && entity.attributes.attribution ? entity.attributes.attribution : undefined;
channel = { channel = {
_id: adapter.namespace + '.entities.' + entity.entity_id, _id: `${adapter.namespace}.entities.${entity.entity_id}`,
common: { common: {
name: name name: name
}, },
@@ -224,15 +214,15 @@ function parseStates(entities, services, callback) {
if (desc) channel.common.desc = desc; if (desc) channel.common.desc = desc;
objs.push(channel); objs.push(channel);
var lc = entity.last_changed ? new Date(entity.last_changed).getTime() : undefined; const lc = entity.last_changed ? new Date(entity.last_changed).getTime() : undefined;
var ts = entity.last_updated ? new Date(entity.last_updated).getTime() : undefined; const ts = entity.last_updated ? new Date(entity.last_updated).getTime() : undefined;
if (entity.state !== undefined) { if (entity.state !== undefined) {
obj = { obj = {
_id: adapter.namespace + '.entities.' + entity.entity_id + '.state', _id: `${adapter.namespace}.entities.${entity.entity_id}.state`,
type: 'state', type: 'state',
common: { common: {
name: name + ' STATE', name: `${name} STATE`,
read: true, read: true,
write: false write: false
}, },
@@ -250,11 +240,13 @@ function parseStates(entities, services, callback) {
} }
if (entity.attributes) { if (entity.attributes) {
for (var attr in entity.attributes) { for (const attr in entity.attributes) {
if (entity.attributes.hasOwnProperty(attr)) { if (entity.attributes.hasOwnProperty(attr)) {
if (attr === 'friendly_name' || attr === 'unit_of_measurement' || attr === 'icon') continue; if (attr === 'friendly_name' || attr === 'unit_of_measurement' || attr === 'icon') {
continue;
}
var common; let common;
if (knownAttributes[attr]) { if (knownAttributes[attr]) {
common = Object.assign({}, knownAttributes[attr]); common = Object.assign({}, knownAttributes[attr]);
} else { } else {
@@ -262,7 +254,7 @@ function parseStates(entities, services, callback) {
} }
obj = { obj = {
_id: adapter.namespace + '.entities.' + entity.entity_id + '.' + attr, _id: `${adapter.namespace}.entities.${entity.entity_id}.${attr}`,
type: 'state', type: 'state',
common: common, common: common,
native: { native: {
@@ -291,14 +283,14 @@ function parseStates(entities, services, callback) {
} }
} }
var serviceType = entity.entity_id.split('.')[0]; const serviceType = entity.entity_id.split('.')[0];
if (services[serviceType] && skipServices.indexOf(serviceType) === -1) { if (services[serviceType] && !skipServices.includes(serviceType)) {
var service = services[serviceType]; const service = services[serviceType];
for (var s in service) { for (const s in service) {
if (service.hasOwnProperty(s)) { if (service.hasOwnProperty(s)) {
obj = { obj = {
_id: adapter.namespace + '.entities.' + entity.entity_id + '.' + s, _id: `${adapter.namespace}.entities.${entity.entity_id}.${s}`,
type: 'state', type: 'state',
common: { common: {
desc: service[s].description, desc: service[s].description,
@@ -320,9 +312,8 @@ function parseStates(entities, services, callback) {
} }
} }
syncObjects(objs, function () { syncObjects(objs, () =>
syncStates(states, callback); syncStates(states, callback));
});
} }
function main() { function main() {
@@ -336,60 +327,60 @@ function main() {
hass = new HASS(adapter.config, adapter.log); hass = new HASS(adapter.config, adapter.log);
hass.on('error', function (err) { hass.on('error', err =>
adapter.log.error(err); adapter.log.error(err));
});
hass.on('state_changed', function (entity) { hass.on('state_changed', entity => {
var id = adapter.namespace + '.entities.' + entity.entity_id + '.'; const id = adapter.namespace + '.entities.' + entity.entity_id + '.';
var lc = entity.last_changed ? new Date(entity.last_changed).getTime() : undefined; const lc = entity.last_changed ? new Date(entity.last_changed).getTime() : undefined;
var ts = entity.last_updated ? new Date(entity.last_updated).getTime() : undefined; const ts = entity.last_updated ? new Date(entity.last_updated).getTime() : undefined;
if (entity.state !== undefined) { if (entity.state !== undefined) {
adapter.setForeignState(id + 'state', {val: entity.state, ack: true, lc: lc, ts: ts}); adapter.setForeignState(id + 'state', {val: entity.state, ack: true, lc: lc, ts: ts});
} }
if (entity.attributes) { if (entity.attributes) {
for (var attr in entity.attributes) { for (const attr in entity.attributes) {
if (!entity.attributes.hasOwnProperty(attr) || attr === 'friendly_name' || attr === 'unit_of_measurement' || attr === 'icon') continue; if (!entity.attributes.hasOwnProperty(attr) || attr === 'friendly_name' || attr === 'unit_of_measurement' || attr === 'icon') {
continue;
}
adapter.setForeignState(id + attr, {val: entity.attributes[attr], ack: true, lc: lc, ts: ts}); adapter.setForeignState(id + attr, {val: entity.attributes[attr], ack: true, lc: lc, ts: ts});
} }
} }
}); });
hass.on('connected', function () { hass.on('connected', () => {
if (!connected) { if (!connected) {
adapter.log.debug('Connected'); adapter.log.debug('Connected');
connected = true; connected = true;
adapter.setState('info.connection', true, true); adapter.setState('info.connection', true, true);
hass.getConfig(function (err, config) { hass.getConfig((err, config) => {
if (err) { if (err) {
adapter.log.error('Cannot read config: ' + err); adapter.log.error('Cannot read config: ' + err);
return; return;
} }
//adapter.log.debug(JSON.stringify(config)); //adapter.log.debug(JSON.stringify(config));
setTimeout(function () { setTimeout(() =>
hass.getStates(function (err, states) { hass.getStates((err, states) => {
if (err) { if (err) {
return adapter.log.error('Cannot read states: ' + err); return adapter.log.error('Cannot read states: ' + err);
} }
//adapter.log.debug(JSON.stringify(states)); //adapter.log.debug(JSON.stringify(states));
setTimeout(function () { setTimeout(() =>
hass.getServices(function (err, services) { hass.getServices((err, services) => {
if (err) { if (err) {
adapter.log.error('Cannot read states: ' + err); adapter.log.error('Cannot read states: ' + err);
} else { } else {
//adapter.log.debug(JSON.stringify(services)); //adapter.log.debug(JSON.stringify(services));
parseStates(states, services, function () { parseStates(states, services, () => {
}); });
} }
}); }), 100);
}, 100); }), 100);
});
}, 100);
}); });
} }
}); });
hass.on('disconnected', function () { hass.on('disconnected', () => {
if (connected) { if (connected) {
adapter.log.debug('Disconnected'); adapter.log.debug('Disconnected');
connected = false; connected = false;
@@ -399,3 +390,11 @@ function main() {
hass.connect(); hass.connect();
} }
// If started as allInOne/compact mode => return function to create instance
if (module && module.parent) {
module.exports = startAdapter;
} else {
// or start the instance directly
startAdapter();
}
+10 -7
View File
@@ -1,6 +1,6 @@
{ {
"name": "iobroker.hass", "name": "iobroker.hass",
"version": "0.1.0", "version": "1.0.0",
"description": "Home Assistant", "description": "Home Assistant",
"author": { "author": {
"name": "bluefox", "name": "bluefox",
@@ -26,17 +26,20 @@
}, },
"dependencies": { "dependencies": {
"home-assistant-js-websocket": "^1.1.4", "home-assistant-js-websocket": "^1.1.4",
"websocket": "^1.0.25", "websocket": "^1.0.33",
"ws": "^5.0.0" "ws": "^5.0.0",
"@iobroker/adapter-core": "^2.4.0"
}, },
"devDependencies": { "devDependencies": {
"gulp": "^3.9.1", "@alcalzone/release-script": "^1.8.1",
"mocha": "^4.1.0", "gulp": "^4.0.2",
"chai": "^4.1.2" "mocha": "^8.2.1",
"chai": "^4.2.0"
}, },
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"test": "node node_modules/mocha/bin/mocha --exit" "test": "node node_modules/mocha/bin/mocha --exit",
"release": "node node_modules/@alcalzone/release-script/bin/release.js"
}, },
"bugs": { "bugs": {
"url": "https://github.com/ioBroker/ioBroker.hass/issues" "url": "https://github.com/ioBroker/ioBroker.hass/issues"