* (Apollon77) Implement Service triggers to use any value to trigger or stringified JSON to call with fields
This commit is contained in:
@@ -28,7 +28,7 @@ jobs:
|
|||||||
node-version: [12.x]
|
node-version: [12.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
@@ -140,19 +140,17 @@ jobs:
|
|||||||
prerelease: ${{ contains(steps.extract_release.outputs.VERSION, '-') }}
|
prerelease: ${{ contains(steps.extract_release.outputs.VERSION, '-') }}
|
||||||
body: ${{ steps.extract_release.outputs.BODY }}
|
body: ${{ steps.extract_release.outputs.BODY }}
|
||||||
|
|
||||||
#- name: Notify Sentry.io about the release
|
- name: Notify Sentry.io about the release
|
||||||
# run: |
|
run: |
|
||||||
# npm i -g @sentry/cli
|
npm i -g @sentry/cli
|
||||||
# export SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
|
export SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
# export SENTRY_URL=https://sentry.iobroker.net
|
export SENTRY_URL=https://sentry.iobroker.net
|
||||||
# export SENTRY_ORG=iobroker
|
export SENTRY_ORG=iobroker
|
||||||
# export SENTRY_PROJECT=iobroker-hass
|
export SENTRY_PROJECT=iobroker-hass
|
||||||
# export SENTRY_VERSION=iobroker.hass@${{ steps.extract_release.outputs.VERSION }}
|
export SENTRY_VERSION=iobroker.hass@${{ steps.extract_release.outputs.VERSION }}
|
||||||
# sentry-cli releases new $SENTRY_VERSION
|
sentry-cli releases new $SENTRY_VERSION
|
||||||
# sentry-cli releases finalize $SENTRY_VERSION
|
sentry-cli releases set-commits $SENTRY_VERSION --auto
|
||||||
|
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
|
# Add the following line BEFORE finalize if sourcemap uploads are needed
|
||||||
# sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/
|
# sentry-cli releases files $SENTRY_VERSION upload-sourcemaps build/
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["iobroker", "license"]
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2018-2020 bluefox <dogafox@gmail.com>
|
Copyright (c) 2018-2022 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,9 +1,16 @@
|
|||||||

|

|
||||||
# ioBroker.hass
|
# ioBroker.hass
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
[](https://www.npmjs.com/package/iobroker.hass)
|
[](https://www.npmjs.com/package/iobroker.hass)
|
||||||
|
|
||||||
|

|
||||||
|
[](https://weblate.iobroker.net/engage/adapters/?utm_source=widget)
|
||||||
[](https://www.npmjs.com/package/iobroker.hass)
|
[](https://www.npmjs.com/package/iobroker.hass)
|
||||||
|
|
||||||
|
**This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and for information how to disable the error reporting see [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Sentry reporting is used starting with js-controller 3.0.
|
||||||
|
|
||||||
|
|
||||||
This adapter allows the connecting of Home Assistant to ioBroker.
|
This adapter allows the connecting of Home Assistant to ioBroker.
|
||||||
|
|
||||||
@@ -24,6 +31,11 @@ Please check it https://www.smarthomejetzt.de/mit-iobroker-auf-eine-home-assista
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### __WORK IN PROGRESS__
|
||||||
|
* (Apollon77) Implement Service triggers to use any value to trigger or stringified JSON to call with fields
|
||||||
|
* (Apollon77) Optimize unload handling
|
||||||
|
* (Apollon7) Add Sentry for crash reporting
|
||||||
|
|
||||||
### 1.0.1 (2021-09-04)
|
### 1.0.1 (2021-09-04)
|
||||||
* IMPORTANT: js-controller 2.0 is needed st least!
|
* IMPORTANT: js-controller 2.0 is needed st least!
|
||||||
* (Apollon77) Fix start issue
|
* (Apollon77) Fix start issue
|
||||||
@@ -38,7 +50,7 @@ Please check it https://www.smarthomejetzt.de/mit-iobroker-auf-eine-home-assista
|
|||||||
## License
|
## License
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2018-2020 bluefox <dogafox@gmail.com>
|
Copyright (c) 2018-2022 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
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "FHEM Telnet IP:",
|
||||||
|
"FHEM Telnet Port:": "FHEM Telnet Port:",
|
||||||
|
"FHEM adapter settings": "FHEM Adapter Einstellungen",
|
||||||
|
"Password repeat:": "Kennwort-Woederholung:",
|
||||||
|
"Password:": "Kennwort:",
|
||||||
|
"Passwords missmatch!": "Kennworte sind unterschiedlich!",
|
||||||
|
"Prompt:": "Prompt:",
|
||||||
|
"tooltip_password": "Optional. Nur falls in FHEM konfiguriert",
|
||||||
|
"tooltip_prompt": "Man kann hier ein eigenes Prompt setzen. Default ist 'hass>'"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "FHEM Telnet IP:",
|
||||||
|
"FHEM Telnet Port:": "FHEM Telnet Port:",
|
||||||
|
"FHEM adapter settings": "FHEM adapter settings",
|
||||||
|
"Password repeat:": "Password repeat:",
|
||||||
|
"Password:": "Password:",
|
||||||
|
"Passwords missmatch!": "Passwords missmatch!",
|
||||||
|
"Prompt:": "Prompt:",
|
||||||
|
"tooltip_password": "Optional. Only if FHEM requires password.",
|
||||||
|
"tooltip_prompt": "You can change standard prompt from 'hass>' to something else."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "FHEM Telnet IP:",
|
||||||
|
"FHEM Telnet Port:": "Puerto Telnet FHEM:",
|
||||||
|
"FHEM adapter settings": "Configuración del adaptador FHEM",
|
||||||
|
"Password repeat:": "Repite la contraseña:",
|
||||||
|
"Password:": "Clave:",
|
||||||
|
"Passwords missmatch!": "¡Las contraseñas no coinciden!",
|
||||||
|
"Prompt:": "Inmediato:",
|
||||||
|
"tooltip_password": "Opcional. ",
|
||||||
|
"tooltip_prompt": "Puede cambiar el aviso estándar de 'hass>' a otra cosa."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "IP Telnet FHEM :",
|
||||||
|
"FHEM Telnet Port:": "Port Telnet FHEM :",
|
||||||
|
"FHEM adapter settings": "Paramètres de l'adaptateur FHEM",
|
||||||
|
"Password repeat:": "Répéter le mot de passe :",
|
||||||
|
"Password:": "Mot de passe:",
|
||||||
|
"Passwords missmatch!": "Les mots de passe ne correspondent pas !",
|
||||||
|
"Prompt:": "Rapide:",
|
||||||
|
"tooltip_password": "Optionnel. ",
|
||||||
|
"tooltip_prompt": "Vous pouvez changer l'invite standard de 'hass>' en quelque chose d'autre."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "IP Telnet FHEM:",
|
||||||
|
"FHEM Telnet Port:": "Porta Telnet FHEM:",
|
||||||
|
"FHEM adapter settings": "Impostazioni dell'adattatore FHEM",
|
||||||
|
"Password repeat:": "Ripeti password:",
|
||||||
|
"Password:": "Parola d'ordine:",
|
||||||
|
"Passwords missmatch!": "Le password non corrispondono!",
|
||||||
|
"Prompt:": "Richiesta:",
|
||||||
|
"tooltip_password": "Opzionale. ",
|
||||||
|
"tooltip_prompt": "Puoi cambiare il prompt standard da 'hass>' a qualcos'altro."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "FHEM Telnet-IP:",
|
||||||
|
"FHEM Telnet Port:": "FHEM Telnet-poort:",
|
||||||
|
"FHEM adapter settings": "Instellingen FHEM-adapter",
|
||||||
|
"Password repeat:": "Wachtwoord herhalen:",
|
||||||
|
"Password:": "Wachtwoord:",
|
||||||
|
"Passwords missmatch!": "Wachtwoorden komen niet overeen!",
|
||||||
|
"Prompt:": "Snel:",
|
||||||
|
"tooltip_password": "Optioneel. ",
|
||||||
|
"tooltip_prompt": "U kunt de standaardprompt wijzigen van 'hass>' naar iets anders."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "Adres IP Telnetu FHEM:",
|
||||||
|
"FHEM Telnet Port:": "Port Telnet FHEM:",
|
||||||
|
"FHEM adapter settings": "Ustawienia adaptera FHEM",
|
||||||
|
"Password repeat:": "Powtórz hasło:",
|
||||||
|
"Password:": "Hasło:",
|
||||||
|
"Passwords missmatch!": "Niezgodność haseł!",
|
||||||
|
"Prompt:": "Podpowiedź:",
|
||||||
|
"tooltip_password": "Opcjonalny. ",
|
||||||
|
"tooltip_prompt": "Możesz zmienić standardowy monit z „hass>” na coś innego."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "FHEM Telnet IP:",
|
||||||
|
"FHEM Telnet Port:": "Porta Telnet FHEM:",
|
||||||
|
"FHEM adapter settings": "Configurações do adaptador FHEM",
|
||||||
|
"Password repeat:": "Repetição de senha:",
|
||||||
|
"Password:": "Senha:",
|
||||||
|
"Passwords missmatch!": "As senhas não correspondem!",
|
||||||
|
"Prompt:": "Pronto:",
|
||||||
|
"tooltip_password": "Opcional. ",
|
||||||
|
"tooltip_prompt": "Você pode alterar o prompt padrão de 'hass>' para outra coisa."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "FHEM Telnet IP:",
|
||||||
|
"FHEM Telnet Port:": "FHEM Telnet порт:",
|
||||||
|
"FHEM adapter settings": "Настройки драйвера FHEM",
|
||||||
|
"Password repeat:": "Подтверждение пароля:",
|
||||||
|
"Password:": "Пароль:",
|
||||||
|
"Passwords missmatch!": "Пароли не совпадают!",
|
||||||
|
"Prompt:": "Промпт:",
|
||||||
|
"tooltip_password": "Не обязательно. Только если настроено в FHEM",
|
||||||
|
"tooltip_prompt": "Можно задать собственный промпт. По умолчанию 'hass>'"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"FHEM Telnet IP:": "FHEM 远程登录 IP:",
|
||||||
|
"FHEM Telnet Port:": "FHEM 远程登录端口:",
|
||||||
|
"FHEM adapter settings": "FHEM 适配器设置",
|
||||||
|
"Password repeat:": "密码重复:",
|
||||||
|
"Password:": "密码:",
|
||||||
|
"Passwords missmatch!": "密码不匹配!",
|
||||||
|
"Prompt:": "迅速的:",
|
||||||
|
"tooltip_password": "可选的。",
|
||||||
|
"tooltip_prompt": "您可以将标准提示从“hass>”更改为其他内容。"
|
||||||
|
}
|
||||||
+12
-17
@@ -1,19 +1,14 @@
|
|||||||
|
/*global systemDictionary:true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
systemDictionary = {
|
systemDictionary = {
|
||||||
"FHEM adapter settings":{"de": "FHEM Adapter Einstellungen", "ru": "Настройки драйвера FHEM"},
|
"FHEM Telnet IP:": { "en": "FHEM Telnet IP:", "de": "FHEM Telnet IP:", "ru": "FHEM Telnet IP:", "pt": "FHEM Telnet IP:", "nl": "FHEM Telnet-IP:", "fr": "IP Telnet FHEM :", "it": "IP Telnet FHEM:", "es": "FHEM Telnet IP:", "pl": "Adres IP Telnetu FHEM:", "zh-cn": "FHEM 远程登录 IP:"},
|
||||||
"FHEM Telnet IP:": {"de": "FHEM Telnet IP:", "ru": "FHEM Telnet IP:"},
|
"FHEM Telnet Port:": { "en": "FHEM Telnet Port:", "de": "FHEM Telnet Port:", "ru": "FHEM Telnet порт:", "pt": "Porta Telnet FHEM:", "nl": "FHEM Telnet-poort:", "fr": "Port Telnet FHEM :", "it": "Porta Telnet FHEM:", "es": "Puerto Telnet FHEM:", "pl": "Port Telnet FHEM:", "zh-cn": "FHEM 远程登录端口:"},
|
||||||
"FHEM Telnet Port:": {"en": "FHEM Telnet Port:", "de": "FHEM Telnet Port:", "ru": "FHEM Telnet порт:"},
|
"FHEM adapter settings": { "en": "FHEM adapter settings", "de": "FHEM Adapter Einstellungen", "ru": "Настройки драйвера FHEM", "pt": "Configurações do adaptador FHEM", "nl": "Instellingen FHEM-adapter", "fr": "Paramètres de l'adaptateur FHEM", "it": "Impostazioni dell'adattatore FHEM", "es": "Configuración del adaptador FHEM", "pl": "Ustawienia adaptera FHEM", "zh-cn": "FHEM 适配器设置"},
|
||||||
"Password:": {"en": "Password:", "de": "Kennwort:", "ru": "Пароль:"},
|
"Password repeat:": { "en": "Password repeat:", "de": "Kennwort-Woederholung:", "ru": "Подтверждение пароля:", "pt": "Repetição de senha:", "nl": "Wachtwoord herhalen:", "fr": "Répéter le mot de passe :", "it": "Ripeti password:", "es": "Repite la contraseña:", "pl": "Powtórz hasło:", "zh-cn": "密码重复:"},
|
||||||
"Password repeat:": {"en": "Password repeat:", "de": "Kennwort-Woederholung:", "ru": "Подтверждение пароля:"},
|
"Password:": { "en": "Password:", "de": "Kennwort:", "ru": "Пароль:", "pt": "Senha:", "nl": "Wachtwoord:", "fr": "Mot de passe:", "it": "Parola d'ordine:", "es": "Clave:", "pl": "Hasło:", "zh-cn": "密码:"},
|
||||||
"Passwords missmatch!": {"en": "Passwords missmatch!", "de": "Kennworte sind unterschiedlich!", "ru": "Пароли не совпадают!"},
|
"Passwords missmatch!": { "en": "Passwords missmatch!", "de": "Kennworte sind unterschiedlich!", "ru": "Пароли не совпадают!", "pt": "As senhas não correspondem!", "nl": "Wachtwoorden komen niet overeen!", "fr": "Les mots de passe ne correspondent pas !", "it": "Le password non corrispondono!", "es": "¡Las contraseñas no coinciden!", "pl": "Niezgodność haseł!", "zh-cn": "密码不匹配!"},
|
||||||
"Prompt:": {"en": "Prompt:", "de": "Prompt:", "ru": "Промпт:"},
|
"Prompt:": { "en": "Prompt:", "de": "Prompt:", "ru": "Промпт:", "pt": "Pronto:", "nl": "Snel:", "fr": "Rapide:", "it": "Richiesta:", "es": "Inmediato:", "pl": "Podpowiedź:", "zh-cn": "迅速的:"},
|
||||||
"tooltip_password": {
|
"tooltip_password": { "en": "Optional. Only if FHEM requires password.", "de": "Optional. Nur falls in FHEM konfiguriert", "ru": "Не обязательно. Только если настроено в FHEM", "pt": "Opcional. ", "nl": "Optioneel. ", "fr": "Optionnel. ", "it": "Opzionale. ", "es": "Opcional. ", "pl": "Opcjonalny. ", "zh-cn": "可选的。"},
|
||||||
"en": "Optional. Only if FHEM requires password.",
|
"tooltip_prompt": { "en": "You can change standard prompt from 'hass>' to something else.", "de": "Man kann hier ein eigenes Prompt setzen. Default ist 'hass>'", "ru": "Можно задать собственный промпт. По умолчанию 'hass>'", "pt": "Você pode alterar o prompt padrão de 'hass>' para outra coisa.", "nl": "U kunt de standaardprompt wijzigen van 'hass>' naar iets anders.", "fr": "Vous pouvez changer l'invite standard de 'hass>' en quelque chose d'autre.", "it": "Puoi cambiare il prompt standard da 'hass>' a qualcos'altro.", "es": "Puede cambiar el aviso estándar de 'hass>' a otra cosa.", "pl": "Możesz zmienić standardowy monit z „hass>” na coś innego.", "zh-cn": "您可以将标准提示从“hass>”更改为其他内容。"},
|
||||||
"de": "Optional. Nur falls in FHEM konfiguriert",
|
|
||||||
"ru": "Не обязательно. Только если настроено в FHEM"
|
|
||||||
},
|
|
||||||
"tooltip_prompt": {
|
|
||||||
"en": "You can change standard prompt from 'hass>' to something else.",
|
|
||||||
"de": "Man kann hier ein eigenes Prompt setzen. Default ist 'hass>'",
|
|
||||||
"ru": "Можно задать собственный промпт. По умолчанию 'hass>'"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
+131
-197
@@ -1,19 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* ioBroker gulpfile
|
||||||
|
* Date: 2019-01-28
|
||||||
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const pkg = require('./package.json');
|
const pkg = require('./package.json');
|
||||||
const iopackage = require('./io-package.json');
|
const iopackage = require('./io-package.json');
|
||||||
const version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
|
const version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
|
||||||
/*var appName = getAppName();
|
|
||||||
|
|
||||||
function getAppName() {
|
|
||||||
var parts = __dirname.replace(/\\/g, '/').split('/');
|
|
||||||
return parts[parts.length - 1].split('.')[0].toLowerCase();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const fileName = 'words.js';
|
const fileName = 'words.js';
|
||||||
let languages = {
|
const EMPTY = '';
|
||||||
|
const translate = require('./lib/tools').translateText;
|
||||||
|
const languages = {
|
||||||
en: {},
|
en: {},
|
||||||
de: {},
|
de: {},
|
||||||
ru: {},
|
ru: {},
|
||||||
@@ -26,25 +25,18 @@ let languages = {
|
|||||||
'zh-cn': {}
|
'zh-cn': {}
|
||||||
};
|
};
|
||||||
|
|
||||||
function lang2data(lang, isFlat) {
|
function lang2data(lang) {
|
||||||
let str = isFlat ? '' : '{\n';
|
let str ='{\n';
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const w in lang) {
|
for (const w in lang) {
|
||||||
if (lang.hasOwnProperty(w)) {
|
if (lang.hasOwnProperty(w)) {
|
||||||
count++;
|
count++;
|
||||||
if (isFlat) {
|
const key = ' "' + w.replace(/"/g, '\\"') + '": ';
|
||||||
str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n';
|
str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
|
||||||
} else {
|
|
||||||
const key = ' "' + w.replace(/"/g, '\\"') + '": ';
|
|
||||||
str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!count) {
|
if (!count) {
|
||||||
return isFlat ? '' : '{\n}';
|
return '{\n}';
|
||||||
}
|
|
||||||
if (isFlat) {
|
|
||||||
return str;
|
|
||||||
} else {
|
} else {
|
||||||
return str.substring(0, str.length - 2) + '\n}';
|
return str.substring(0, str.length - 2) + '\n}';
|
||||||
}
|
}
|
||||||
@@ -58,26 +50,9 @@ function readWordJs(src) {
|
|||||||
} else {
|
} else {
|
||||||
words = fs.readFileSync(src + fileName).toString();
|
words = fs.readFileSync(src + fileName).toString();
|
||||||
}
|
}
|
||||||
|
words = words.substring(words.indexOf('{'), words.length);
|
||||||
|
words = words.substring(0, words.lastIndexOf(';'));
|
||||||
|
|
||||||
const lines = words.split(/\r\n|\r|\n/g);
|
|
||||||
let i = 0;
|
|
||||||
while (!lines[i].match(/^systemDictionary = {/)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
lines.splice(0, i);
|
|
||||||
|
|
||||||
// remove last empty lines
|
|
||||||
i = lines.length - 1;
|
|
||||||
while (!lines[i]) {
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
if (i < lines.length - 1) {
|
|
||||||
lines.splice(i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines[0] = lines[0].replace('systemDictionary = ', '');
|
|
||||||
lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}');
|
|
||||||
words = lines.join('\n');
|
|
||||||
const resultFunc = new Function('return ' + words + ';');
|
const resultFunc = new Function('return ' + words + ';');
|
||||||
|
|
||||||
return resultFunc();
|
return resultFunc();
|
||||||
@@ -85,14 +60,15 @@ function readWordJs(src) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function padRight(text, totalLength) {
|
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) {
|
|
||||||
let text = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n';
|
|
||||||
text += '/*global systemDictionary:true */\n';
|
|
||||||
text += '\'use strict\';\n\n';
|
|
||||||
|
|
||||||
|
function writeWordJs(data, src) {
|
||||||
|
let text = '';
|
||||||
|
text += '/*global systemDictionary:true */\n';
|
||||||
|
text += "'use strict';\n\n";
|
||||||
text += 'systemDictionary = {\n';
|
text += 'systemDictionary = {\n';
|
||||||
for (const word in data) {
|
for (const word in data) {
|
||||||
if (data.hasOwnProperty(word)) {
|
if (data.hasOwnProperty(word)) {
|
||||||
@@ -110,9 +86,7 @@ function writeWordJs(data, src) {
|
|||||||
text += line + '},\n';
|
text += line + '},\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text = text.replace(/},\n$/, '}\n');
|
|
||||||
text += '};';
|
text += '};';
|
||||||
|
|
||||||
if (fs.existsSync(src + 'js/' + fileName)) {
|
if (fs.existsSync(src + 'js/' + fileName)) {
|
||||||
fs.writeFileSync(src + 'js/' + fileName, text);
|
fs.writeFileSync(src + 'js/' + fileName, text);
|
||||||
} else {
|
} else {
|
||||||
@@ -120,8 +94,6 @@ function writeWordJs(data, src) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY = '';
|
|
||||||
|
|
||||||
function words2languages(src) {
|
function words2languages(src) {
|
||||||
const langs = Object.assign({}, languages);
|
const langs = Object.assign({}, languages);
|
||||||
const data = readWordJs(src);
|
const data = readWordJs(src);
|
||||||
@@ -145,10 +117,11 @@ function words2languages(src) {
|
|||||||
fs.mkdirSync(src + 'i18n/');
|
fs.mkdirSync(src + 'i18n/');
|
||||||
}
|
}
|
||||||
for (const l in langs) {
|
for (const l in langs) {
|
||||||
if (!langs.hasOwnProperty(l)) continue;
|
if (!langs.hasOwnProperty(l))
|
||||||
|
continue;
|
||||||
const keys = Object.keys(langs[l]);
|
const keys = Object.keys(langs[l]);
|
||||||
//keys.sort();
|
keys.sort();
|
||||||
let obj = {};
|
const obj = {};
|
||||||
for (let 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]];
|
||||||
}
|
}
|
||||||
@@ -162,144 +135,36 @@ function words2languages(src) {
|
|||||||
console.error('Cannot read or parse ' + fileName);
|
console.error('Cannot read or parse ' + fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function words2languagesFlat(src) {
|
|
||||||
let langs = Object.assign({}, languages);
|
|
||||||
const data = readWordJs(src);
|
|
||||||
if (data) {
|
|
||||||
for (const word in data) {
|
|
||||||
if (data.hasOwnProperty(word)) {
|
|
||||||
for (const lang in data[word]) {
|
|
||||||
if (data[word].hasOwnProperty(lang)) {
|
|
||||||
langs[lang][word] = data[word][lang];
|
|
||||||
// pre-fill all other languages
|
|
||||||
for (const j in langs) {
|
|
||||||
if (langs.hasOwnProperty(j)) {
|
|
||||||
langs[j][word] = langs[j][word] || EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const keys = Object.keys(langs.en);
|
|
||||||
//keys.sort();
|
|
||||||
for (const l in langs) {
|
|
||||||
if (!langs.hasOwnProperty(l)) continue;
|
|
||||||
let obj = {};
|
|
||||||
for (let k = 0; k < keys.length; k++) {
|
|
||||||
obj[keys[k]] = langs[l][keys[k]];
|
|
||||||
}
|
|
||||||
langs[l] = obj;
|
|
||||||
}
|
|
||||||
if (!fs.existsSync(src + 'i18n/')) {
|
|
||||||
fs.mkdirSync(src + 'i18n/');
|
|
||||||
}
|
|
||||||
for (const ll in langs) {
|
|
||||||
if (!langs.hasOwnProperty(ll)) continue;
|
|
||||||
if (!fs.existsSync(src + 'i18n/' + ll)) {
|
|
||||||
fs.mkdirSync(src + 'i18n/' + ll);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(src + 'i18n/' + ll + '/flat.txt', lang2data(langs[ll], langs.en));
|
|
||||||
}
|
|
||||||
fs.writeFileSync(src + 'i18n/flat.txt', keys.join('\n'));
|
|
||||||
} else {
|
|
||||||
console.error('Cannot read or parse ' + fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function languagesFlat2words(src) {
|
|
||||||
const dirs = fs.readdirSync(src + 'i18n/');
|
|
||||||
let langs = {};
|
|
||||||
let bigOne = {};
|
|
||||||
const order = Object.keys(languages);
|
|
||||||
dirs.sort((a, b) => {
|
|
||||||
const posA = order.indexOf(a);
|
|
||||||
const posB = order.indexOf(b);
|
|
||||||
if (posA === -1 && posB === -1) {
|
|
||||||
if (a > b) return 1;
|
|
||||||
if (a < b) return -1;
|
|
||||||
return 0;
|
|
||||||
} else if (posA === -1) {
|
|
||||||
return -1;
|
|
||||||
} else if (posB === -1) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
if (posA > posB) return 1;
|
|
||||||
if (posA < posB) return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n');
|
|
||||||
|
|
||||||
for (let l = 0; l < dirs.length; l++) {
|
|
||||||
if (dirs[l] === 'flat.txt') continue;
|
|
||||||
let lang = dirs[l];
|
|
||||||
const values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n');
|
|
||||||
langs[lang] = {};
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
|
|
||||||
const words = langs[lang];
|
|
||||||
for (const word in words) {
|
|
||||||
if (words.hasOwnProperty(word)) {
|
|
||||||
bigOne[word] = bigOne[word] || {};
|
|
||||||
if (words[word] !== EMPTY) {
|
|
||||||
bigOne[word][lang] = words[word];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// read actual words.js
|
|
||||||
const aWords = readWordJs();
|
|
||||||
|
|
||||||
const temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt'];
|
|
||||||
if (aWords) {
|
|
||||||
// Merge words together
|
|
||||||
for (const w in aWords) {
|
|
||||||
if (aWords.hasOwnProperty(w)) {
|
|
||||||
if (!bigOne[w]) {
|
|
||||||
console.warn('Take from actual words.js: ' + w);
|
|
||||||
bigOne[w] = aWords[w]
|
|
||||||
}
|
|
||||||
dirs.forEach(function (lang) {
|
|
||||||
if (temporaryIgnore.indexOf(lang) !== -1) return;
|
|
||||||
if (!bigOne[w][lang]) {
|
|
||||||
console.warn('Missing "' + lang + '": ' + w);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writeWordJs(bigOne, src);
|
|
||||||
}
|
|
||||||
function languages2words(src) {
|
function languages2words(src) {
|
||||||
const dirs = fs.readdirSync(src + 'i18n/');
|
const dirs = fs.readdirSync(src + 'i18n/');
|
||||||
let langs = {};
|
const langs = {};
|
||||||
let bigOne = {};
|
const bigOne = {};
|
||||||
const order = Object.keys(languages);
|
const order = Object.keys(languages);
|
||||||
dirs.sort((a, b) => {
|
dirs.sort(function (a, b) {
|
||||||
const posA = order.indexOf(a);
|
const posA = order.indexOf(a);
|
||||||
const 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)
|
||||||
if (a < b) return -1;
|
return 1;
|
||||||
|
if (a < b)
|
||||||
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
} else if (posA === -1) {
|
} else if (posA === -1) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (posB === -1) {
|
} else if (posB === -1) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
if (posA > posB) return 1;
|
if (posA > posB)
|
||||||
if (posA < posB) return -1;
|
return 1;
|
||||||
|
if (posA < posB)
|
||||||
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for (let l = 0; l < dirs.length; l++) {
|
for (const lang of dirs) {
|
||||||
if (dirs[l] === 'flat.txt') continue;
|
if (lang === 'flat.txt')
|
||||||
const lang = dirs[l];
|
continue;
|
||||||
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]);
|
||||||
const words = langs[lang];
|
const words = langs[lang];
|
||||||
@@ -315,17 +180,18 @@ function languages2words(src) {
|
|||||||
// read actual words.js
|
// read actual words.js
|
||||||
const aWords = readWordJs();
|
const aWords = readWordJs();
|
||||||
|
|
||||||
const temporaryIgnore = ['pt', 'fr', 'nl', 'it'];
|
const temporaryIgnore = ['flat.txt'];
|
||||||
if (aWords) {
|
if (aWords) {
|
||||||
// Merge words together
|
// Merge words together
|
||||||
for (const 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);
|
||||||
bigOne[w] = aWords[w]
|
bigOne[w] = aWords[w];
|
||||||
}
|
}
|
||||||
dirs.forEach(function (lang) {
|
dirs.forEach(function (lang) {
|
||||||
if (temporaryIgnore.indexOf(lang) !== -1) return;
|
if (temporaryIgnore.indexOf(lang) !== -1)
|
||||||
|
return;
|
||||||
if (!bigOne[w][lang]) {
|
if (!bigOne[w][lang]) {
|
||||||
console.warn('Missing "' + lang + '": ' + w);
|
console.warn('Missing "' + lang + '": ' + w);
|
||||||
}
|
}
|
||||||
@@ -338,37 +204,53 @@ function languages2words(src) {
|
|||||||
writeWordJs(bigOne, src);
|
writeWordJs(bigOne, src);
|
||||||
}
|
}
|
||||||
|
|
||||||
gulp.task('admin (words.js => json)', done => {
|
async function translateNotExisting(obj, baseText, yandex) {
|
||||||
|
let t = obj['en'];
|
||||||
|
if (!t) {
|
||||||
|
t = baseText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t) {
|
||||||
|
for (let l in languages) {
|
||||||
|
if (!obj[l]) {
|
||||||
|
const time = new Date().getTime();
|
||||||
|
obj[l] = await translate(t, l, yandex);
|
||||||
|
console.log('en -> ' + l + ' ' + (new Date().getTime() - time) + ' ms');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TASKS
|
||||||
|
|
||||||
|
gulp.task('adminWords2languages', function (done) {
|
||||||
words2languages('./admin/');
|
words2languages('./admin/');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('admin (words.js => flat)', done => {
|
gulp.task('adminLanguages2words', function (done) {
|
||||||
words2languagesFlat('./admin/');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('admin (flat => words.js)', done => {
|
|
||||||
languagesFlat2words('./admin/');
|
|
||||||
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]) {
|
||||||
const news = iopackage.common.news;
|
const news = iopackage.common.news;
|
||||||
let newNews = {};
|
const newNews = {};
|
||||||
|
|
||||||
newNews[pkg.version] = {
|
newNews[pkg.version] = {
|
||||||
en: 'news',
|
en: 'news',
|
||||||
de: 'neues',
|
de: 'neues',
|
||||||
ru: 'новое'
|
ru: 'новое',
|
||||||
|
pt: 'novidades',
|
||||||
|
nl: 'nieuws',
|
||||||
|
fr: 'nouvelles',
|
||||||
|
it: 'notizie',
|
||||||
|
es: 'noticias',
|
||||||
|
pl: 'nowości',
|
||||||
|
'zh-cn': '新'
|
||||||
};
|
};
|
||||||
iopackage.common.news = Object.assign(newNews, news);
|
iopackage.common.news = Object.assign(newNews, news);
|
||||||
}
|
}
|
||||||
@@ -376,12 +258,12 @@ gulp.task('updatePackages', done => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('updateReadme', done => {
|
gulp.task('updateReadme', function (done) {
|
||||||
const readme = fs.readFileSync('README.md').toString();
|
const readme = fs.readFileSync('README.md').toString();
|
||||||
const pos = readme.indexOf('## Changelog\n');
|
const pos = readme.indexOf('## Changelog\n');
|
||||||
if (pos !== -1) {
|
if (pos !== -1) {
|
||||||
let readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
|
const readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
|
||||||
let readmeEnd = readme.substring(pos + '## Changelog\n'.length);
|
const readmeEnd = readme.substring(pos + '## Changelog\n'.length);
|
||||||
|
|
||||||
if (readme.indexOf(version) === -1) {
|
if (readme.indexOf(version) === -1) {
|
||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
@@ -400,4 +282,56 @@ gulp.task('updateReadme', done => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task('translate', async function (done) {
|
||||||
|
|
||||||
|
let yandex;
|
||||||
|
const i = process.argv.indexOf('--yandex');
|
||||||
|
if (i > -1) {
|
||||||
|
yandex = process.argv[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iopackage && iopackage.common) {
|
||||||
|
if (iopackage.common.news) {
|
||||||
|
console.log('Translate News');
|
||||||
|
for (let k in iopackage.common.news) {
|
||||||
|
console.log('News: ' + k);
|
||||||
|
let nw = iopackage.common.news[k];
|
||||||
|
await translateNotExisting(nw, null, yandex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iopackage.common.titleLang) {
|
||||||
|
console.log('Translate Title');
|
||||||
|
await translateNotExisting(iopackage.common.titleLang, iopackage.common.title, yandex);
|
||||||
|
}
|
||||||
|
if (iopackage.common.desc) {
|
||||||
|
console.log('Translate Description');
|
||||||
|
await translateNotExisting(iopackage.common.desc, null, yandex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync('./admin/i18n/en/translations.json')) {
|
||||||
|
let enTranslations = require('./admin/i18n/en/translations.json');
|
||||||
|
for (let l in languages) {
|
||||||
|
console.log('Translate Text: ' + l);
|
||||||
|
let existing = {};
|
||||||
|
if (fs.existsSync('./admin/i18n/' + l + '/translations.json')) {
|
||||||
|
existing = require('./admin/i18n/' + l + '/translations.json');
|
||||||
|
}
|
||||||
|
for (let t in enTranslations) {
|
||||||
|
if (!existing[t]) {
|
||||||
|
existing[t] = await translate(enTranslations[t], l, yandex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fs.existsSync('./admin/i18n/' + l + '/')) {
|
||||||
|
fs.mkdirSync('./admin/i18n/' + l + '/');
|
||||||
|
}
|
||||||
|
fs.writeFileSync('./admin/i18n/' + l + '/translations.json', JSON.stringify(existing, null, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('translateAndUpdateWordsJS', gulp.series('translate', 'adminLanguages2words', 'adminWords2languages'));
|
||||||
|
|
||||||
gulp.task('default', gulp.series('updatePackages', 'updateReadme'));
|
gulp.task('default', gulp.series('updatePackages', 'updateReadme'));
|
||||||
+9
-2
@@ -24,7 +24,8 @@
|
|||||||
"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",
|
||||||
|
"zh-cn": "ioBroker 的家庭助理连接"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
"1.0.1": {
|
"1.0.1": {
|
||||||
@@ -60,7 +61,8 @@
|
|||||||
"fr": "version initiale",
|
"fr": "version initiale",
|
||||||
"it": "versione iniziale",
|
"it": "versione iniziale",
|
||||||
"es": "versión inicial",
|
"es": "versión inicial",
|
||||||
"pl": "początkowa wersja"
|
"pl": "początkowa wersja",
|
||||||
|
"zh-cn": "初始版本"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"localLink": "http://%host%:8123",
|
"localLink": "http://%host%:8123",
|
||||||
@@ -88,6 +90,11 @@
|
|||||||
"js-controller": ">=2.0.0"
|
"js-controller": ">=2.0.0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"plugins": {
|
||||||
|
"sentry": {
|
||||||
|
"dsn": "https://1b2a1e8335f84cc49a67dd5ac2b07573@sentry.iobroker.net/182"
|
||||||
|
}
|
||||||
|
},
|
||||||
"connectionType": "local",
|
"connectionType": "local",
|
||||||
"dataSource": "push",
|
"dataSource": "push",
|
||||||
"tier": 2
|
"tier": 2
|
||||||
|
|||||||
+5
-4
@@ -92,7 +92,7 @@ function HASS(options, log) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function callService(socket, service, domain, serviceData, callback) {
|
function callService(socket, service, domain, serviceData, target, callback) {
|
||||||
if (socket && typeof socket.send === 'function') {
|
if (socket && typeof socket.send === 'function') {
|
||||||
const 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()};
|
||||||
@@ -101,7 +101,8 @@ function HASS(options, log) {
|
|||||||
type: 'call_service',
|
type: 'call_service',
|
||||||
domain: domain || '',
|
domain: domain || '',
|
||||||
service: service,
|
service: service,
|
||||||
service_data: serviceData
|
service_data: serviceData,
|
||||||
|
target
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
callback && callback('not connected');
|
callback && callback('not connected');
|
||||||
@@ -228,11 +229,11 @@ function HASS(options, log) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.callService = function (service, domain, serviceData, callback) {
|
this.callService = function (service, domain, serviceData, target, callback) {
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
typeof callback === 'function' && callback('not connected');
|
typeof callback === 'function' && callback('not connected');
|
||||||
} else {
|
} else {
|
||||||
callService(this.socket, service, domain, serviceData, callback);
|
callService(this.socket, service, domain, serviceData, target, callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the given variable is a real object and not an Array
|
||||||
|
* @param {any} it The variable to test
|
||||||
|
* @returns {it is Record<string, any>}
|
||||||
|
*/
|
||||||
|
function isObject(it) {
|
||||||
|
// This is necessary because:
|
||||||
|
// typeof null === 'object'
|
||||||
|
// typeof [] === 'object'
|
||||||
|
// [] instanceof Object === true
|
||||||
|
return Object.prototype.toString.call(it) === "[object Object]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the given variable is really an Array
|
||||||
|
* @param {any} it The variable to test
|
||||||
|
* @returns {it is any[]}
|
||||||
|
*/
|
||||||
|
function isArray(it) {
|
||||||
|
if (Array.isArray != null)
|
||||||
|
return Array.isArray(it);
|
||||||
|
return Object.prototype.toString.call(it) === "[object Array]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose the right tranalation API
|
||||||
|
* @param {string} text The text to translate
|
||||||
|
* @param {string} targetLang The target languate
|
||||||
|
* @param {string} yandex api key
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function translateText(text, targetLang, yandex) {
|
||||||
|
if (targetLang === "en") {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
if (yandex) {
|
||||||
|
return await translateYandex(text, targetLang, yandex);
|
||||||
|
} else {
|
||||||
|
return await translateGoogle(text, targetLang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates text with Yandex API
|
||||||
|
* @param {string} text The text to translate
|
||||||
|
* @param {string} targetLang The target languate
|
||||||
|
* @param {string} yandex api key
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function translateYandex(text, targetLang, yandex) {
|
||||||
|
if (targetLang === "zh-cn") {
|
||||||
|
targetLang = "zh";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const url = `https://translate.yandex.net/api/v1.5/tr.json/translate?key=${yandex}&text=${encodeURIComponent(text)}&lang=en-${targetLang}`;
|
||||||
|
const response = await axios({url, timeout: 15000});
|
||||||
|
if (response.data && response.data['text']) {
|
||||||
|
return response.data['text'][0];
|
||||||
|
}
|
||||||
|
throw new Error("Invalid response for translate request");
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Could not translate to "${targetLang}": ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates text with Google API
|
||||||
|
* @param {string} text The text to translate
|
||||||
|
* @param {string} targetLang The target languate
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function translateGoogle(text, targetLang) {
|
||||||
|
try {
|
||||||
|
const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
|
||||||
|
const response = await axios({url, timeout: 15000});
|
||||||
|
if (isArray(response.data)) {
|
||||||
|
// we got a valid response
|
||||||
|
return response.data[0][0][0];
|
||||||
|
}
|
||||||
|
throw new Error("Invalid response for translate request");
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Could not translate to "${targetLang}": ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
isArray,
|
||||||
|
isObject,
|
||||||
|
translateText
|
||||||
|
};
|
||||||
@@ -12,6 +12,8 @@ let connected = false;
|
|||||||
let hass;
|
let hass;
|
||||||
let adapter;
|
let adapter;
|
||||||
const hassObjects = {};
|
const hassObjects = {};
|
||||||
|
let delayTimeout = null;
|
||||||
|
let stopped = false;
|
||||||
|
|
||||||
function startAdapter(options) {
|
function startAdapter(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
@@ -35,6 +37,17 @@ function startAdapter(options) {
|
|||||||
} else {
|
} else {
|
||||||
const serviceData = {};
|
const serviceData = {};
|
||||||
const fields = hassObjects[id].native.fields;
|
const fields = hassObjects[id].native.fields;
|
||||||
|
const target = {};
|
||||||
|
|
||||||
|
let requestFields = {};
|
||||||
|
if (typeof state.val === 'string') {
|
||||||
|
try {
|
||||||
|
requestFields = JSON.parse(state.val);
|
||||||
|
} catch (err) {
|
||||||
|
adapter.log.info(`Ignore data for service call ${id} is no valid JSON: ${err.message}`);
|
||||||
|
requestFields = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const field in fields) {
|
for (const field in fields) {
|
||||||
if (!fields.hasOwnProperty(field)) {
|
if (!fields.hasOwnProperty(field)) {
|
||||||
@@ -42,15 +55,15 @@ function startAdapter(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (field === 'entity_id') {
|
if (field === 'entity_id') {
|
||||||
serviceData.entity_id = hassObjects[id].native.entity_id
|
target.entity_id = hassObjects[id].native.entity_id
|
||||||
} else {
|
} else if (requestFields[field] !== undefined) {
|
||||||
serviceData[field] = state.val;
|
serviceData[field] = requestFields[field];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serviceData.entity_id = hassObjects[id].native.entity_id
|
serviceData.entity_id = hassObjects[id].native.entity_id
|
||||||
|
|
||||||
adapter.log.debug(`Send to HASS for service ${hassObjects[id].native.attr} with ${hassObjects[id].native.domain || hassObjects[id].native.type} and data ${JSON.stringify(serviceData)}`)
|
adapter.log.debug(`Send to HASS for service ${hassObjects[id].native.attr} with ${hassObjects[id].native.domain || hassObjects[id].native.type} and data ${JSON.stringify(serviceData)}`)
|
||||||
hass.callService(hassObjects[id].native.attr, hassObjects[id].native.domain || hassObjects[id].native.type, serviceData, err =>
|
hass.callService(hassObjects[id].native.attr, hassObjects[id].native.domain || hassObjects[id].native.type, serviceData, target, err =>
|
||||||
err && adapter.log.error('Cannot control ' + id + ': ' + err));
|
err && adapter.log.error('Cannot control ' + id + ': ' + err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +78,8 @@ function startAdapter(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stop(callback) {
|
function stop(callback) {
|
||||||
|
stopped = true;
|
||||||
|
delayTimeout && clearTimeout(delayTimeout);
|
||||||
hass && hass.close();
|
hass && hass.close();
|
||||||
callback && callback();
|
callback && callback();
|
||||||
}
|
}
|
||||||
@@ -318,7 +333,8 @@ function parseStates(entities, services, callback) {
|
|||||||
common: {
|
common: {
|
||||||
desc: service[s].description,
|
desc: service[s].description,
|
||||||
read: false,
|
read: false,
|
||||||
write: true
|
write: true,
|
||||||
|
type: 'mixed'
|
||||||
},
|
},
|
||||||
native: {
|
native: {
|
||||||
object_id: entity.object_id,
|
object_id: entity.object_id,
|
||||||
@@ -386,14 +402,22 @@ function main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//adapter.log.debug(JSON.stringify(config));
|
//adapter.log.debug(JSON.stringify(config));
|
||||||
setTimeout(() =>
|
delayTimeout = setTimeout(() => {
|
||||||
hass.getStates((err, states) => {
|
delayTimeout = null;
|
||||||
|
!stopped && hass.getStates((err, states) => {
|
||||||
|
if (stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
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(() =>
|
delayTimeout = setTimeout(() => {
|
||||||
hass.getServices((err, services) => {
|
delayTimeout = null;
|
||||||
|
!stopped && hass.getServices((err, services) => {
|
||||||
|
if (stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
adapter.log.error('Cannot read states: ' + err);
|
adapter.log.error('Cannot read states: ' + err);
|
||||||
} else {
|
} else {
|
||||||
@@ -403,8 +427,8 @@ function main() {
|
|||||||
adapter.subscribeStates('*');
|
adapter.subscribeStates('*');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}), 100);
|
})}, 100);
|
||||||
}), 100);
|
})}, 100);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+9
-3
@@ -30,15 +30,21 @@
|
|||||||
"@iobroker/adapter-core": "^2.6.0"
|
"@iobroker/adapter-core": "^2.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@alcalzone/release-script": "^2.2.1",
|
"@alcalzone/release-script": "^3.5.6",
|
||||||
|
"@alcalzone/release-script-plugin-iobroker": "^3.5.7",
|
||||||
|
"@alcalzone/release-script-plugin-license": "^3.5.3",
|
||||||
|
"axios": "^0.26.1",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"mocha": "^9.2.1",
|
"mocha": "^9.2.2",
|
||||||
"chai": "^4.3.6"
|
"chai": "^4.3.6"
|
||||||
},
|
},
|
||||||
"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"
|
"release": "release-script",
|
||||||
|
"release-patch": "release-script patch --yes",
|
||||||
|
"release-minor": "release-script minor --yes",
|
||||||
|
"release-major": "release-script major --yes"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/ioBroker/ioBroker.hass/issues"
|
"url": "https://github.com/ioBroker/ioBroker.hass/issues"
|
||||||
|
|||||||
Reference in New Issue
Block a user