@willieee802/zigbee-herdsman-converters 15.0.8-4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/converters/fromZigbee.js +8439 -0
- package/converters/toZigbee.js +7162 -0
- package/devices/ITCommander.js +37 -0
- package/devices/RMC002.js +20 -0
- package/devices/acova.js +101 -0
- package/devices/acuity_brands_lighting.js +11 -0
- package/devices/adeo.js +256 -0
- package/devices/adurosmart.js +130 -0
- package/devices/aeotec.js +13 -0
- package/devices/airam.js +59 -0
- package/devices/ajax_online.js +49 -0
- package/devices/akuvox.js +28 -0
- package/devices/alchemy.js +18 -0
- package/devices/aldi.js +53 -0
- package/devices/alecto.js +98 -0
- package/devices/anchor.js +17 -0
- package/devices/atlantic.js +110 -0
- package/devices/atsmart.js +28 -0
- package/devices/aubess.js +21 -0
- package/devices/aurora_lighting.js +292 -0
- package/devices/automaton.js +44 -0
- package/devices/awox.js +184 -0
- package/devices/axis.js +22 -0
- package/devices/bankamp.js +11 -0
- package/devices/bega.js +22 -0
- package/devices/belkin.js +11 -0
- package/devices/bitron.js +334 -0
- package/devices/blaupunkt.js +27 -0
- package/devices/blitzwolf.js +47 -0
- package/devices/bosch.js +702 -0
- package/devices/brimate.js +15 -0
- package/devices/bseed.js +17 -0
- package/devices/bticino.js +118 -0
- package/devices/busch-jaeger.js +140 -0
- package/devices/byun.js +24 -0
- package/devices/calex.js +37 -0
- package/devices/candeo.js +49 -0
- package/devices/casaia.js +52 -0
- package/devices/centralite.js +359 -0
- package/devices/cleode.js +20 -0
- package/devices/cleverio.js +36 -0
- package/devices/climax.js +138 -0
- package/devices/commercial_electric.js +16 -0
- package/devices/connecte.js +46 -0
- package/devices/cree.js +11 -0
- package/devices/ctm.js +999 -0
- package/devices/current_products_corp.js +24 -0
- package/devices/custom_devices_diy.js +850 -0
- package/devices/cy-lighting.js +11 -0
- package/devices/danalock.js +26 -0
- package/devices/danfoss.js +340 -0
- package/devices/databyte.ch.js +55 -0
- package/devices/datek.js +246 -0
- package/devices/dawon_dns.js +338 -0
- package/devices/develco.js +868 -0
- package/devices/digi.js +14 -0
- package/devices/diyruz.js +305 -0
- package/devices/dlink.js +37 -0
- package/devices/dnake.js +11 -0
- package/devices/dresden_elektronik.js +47 -0
- package/devices/easyaccess.js +27 -0
- package/devices/eatonhalo_led.js +11 -0
- package/devices/echostar.js +25 -0
- package/devices/ecodim.js +145 -0
- package/devices/ecolink.js +21 -0
- package/devices/ecosmart.js +64 -0
- package/devices/ecozy.js +31 -0
- package/devices/edp.js +39 -0
- package/devices/eglo.js +25 -0
- package/devices/elko.js +176 -0
- package/devices/enbrighten.js +163 -0
- package/devices/enocean.js +52 -0
- package/devices/envilar.js +73 -0
- package/devices/essentialb.js +87 -0
- package/devices/eurotronic.js +48 -0
- package/devices/evanell.js +29 -0
- package/devices/evn.js +31 -0
- package/devices/evology.js +24 -0
- package/devices/evvr.js +17 -0
- package/devices/ewelink.js +184 -0
- package/devices/ezex.js +24 -0
- package/devices/fantem.js +77 -0
- package/devices/feibit.js +172 -0
- package/devices/fireangel.js +15 -0
- package/devices/frankever.js +23 -0
- package/devices/garza.js +11 -0
- package/devices/ge.js +111 -0
- package/devices/gewiss.js +48 -0
- package/devices/gidealed.js +11 -0
- package/devices/giderwel.js +11 -0
- package/devices/giex.js +222 -0
- package/devices/girier.js +19 -0
- package/devices/gledopto.js +756 -0
- package/devices/gmy.js +11 -0
- package/devices/gs.js +29 -0
- package/devices/halemeier.js +18 -0
- package/devices/hampton_bay.js +37 -0
- package/devices/heiman.js +805 -0
- package/devices/hej.js +107 -0
- package/devices/hfh.js +11 -0
- package/devices/hgkg.js +54 -0
- package/devices/hilux.js +11 -0
- package/devices/hive.js +524 -0
- package/devices/home_control_as.js +27 -0
- package/devices/hommyn.js +24 -0
- package/devices/honyar.js +29 -0
- package/devices/hornbach.js +53 -0
- package/devices/hzc.js +21 -0
- package/devices/hzc_electric.js +42 -0
- package/devices/icasa.js +121 -0
- package/devices/idinio.js +19 -0
- package/devices/ihorn.js +61 -0
- package/devices/ikea.js +1168 -0
- package/devices/ilightsin.js +11 -0
- package/devices/iluminize.js +262 -0
- package/devices/ilux.js +11 -0
- package/devices/immax.js +203 -0
- package/devices/innr.js +705 -0
- package/devices/inovelli.js +1315 -0
- package/devices/insta.js +110 -0
- package/devices/iolloi.js +20 -0
- package/devices/iotperfect.js +20 -0
- package/devices/iris.js +180 -0
- package/devices/istar.js +20 -0
- package/devices/jasco.js +55 -0
- package/devices/javis.js +37 -0
- package/devices/jethome.js +48 -0
- package/devices/jiawen.js +18 -0
- package/devices/jumitech.js +18 -0
- package/devices/jxuan.js +49 -0
- package/devices/kami.js +15 -0
- package/devices/keen_home.js +80 -0
- package/devices/klikaanklikuit.js +17 -0
- package/devices/kmpcil.js +148 -0
- package/devices/konke.js +141 -0
- package/devices/ksentry.js +17 -0
- package/devices/kurvia.js +17 -0
- package/devices/kwikset.js +120 -0
- package/devices/lanesto.js +11 -0
- package/devices/lds.js +11 -0
- package/devices/led_trading.js +69 -0
- package/devices/ledvance.js +353 -0
- package/devices/leedarson.js +130 -0
- package/devices/legrand.js +554 -0
- package/devices/lellki.js +146 -0
- package/devices/letsled.js +11 -0
- package/devices/letv.js +18 -0
- package/devices/leviton.js +142 -0
- package/devices/lg.js +18 -0
- package/devices/lidl.js +1000 -0
- package/devices/lifecontrol.js +92 -0
- package/devices/lightsolutions.js +37 -0
- package/devices/linkind.js +205 -0
- package/devices/livingwise.js +59 -0
- package/devices/livolo.js +286 -0
- package/devices/lixee.js +779 -0
- package/devices/lonsonho.js +218 -0
- package/devices/lubeez.js +18 -0
- package/devices/lupus.js +74 -0
- package/devices/lutron.js +32 -0
- package/devices/lux.js +40 -0
- package/devices/m-elec.js +18 -0
- package/devices/makegood.js +51 -0
- package/devices/matcall_bv.js +18 -0
- package/devices/meazon.js +47 -0
- package/devices/mercator.js +225 -0
- package/devices/miboxer.js +72 -0
- package/devices/micromatic.js +29 -0
- package/devices/moes.js +400 -0
- package/devices/mycket.js +11 -0
- package/devices/m/303/274ller_licht.js +220 -0
- package/devices/namron.js +774 -0
- package/devices/nanoleaf.js +11 -0
- package/devices/neo.js +86 -0
- package/devices/net2grid.js +29 -0
- package/devices/netvox.js +35 -0
- package/devices/niko.js +314 -0
- package/devices/ninja_blocks.js +24 -0
- package/devices/niviss.js +11 -0
- package/devices/nodon.js +109 -0
- package/devices/nordtronic.js +30 -0
- package/devices/nous.js +88 -0
- package/devices/novo.js +17 -0
- package/devices/nue_3a.js +417 -0
- package/devices/nyce.js +67 -0
- package/devices/onesti.js +59 -0
- package/devices/openlumi.js +22 -0
- package/devices/orvibo.js +515 -0
- package/devices/osram.js +519 -0
- package/devices/oujiabao.js +15 -0
- package/devices/owon.js +352 -0
- package/devices/ozsmartthings.js +11 -0
- package/devices/paul_neuhaus.js +96 -0
- package/devices/paulmann.js +158 -0
- package/devices/peq.js +24 -0
- package/devices/perenio.js +413 -0
- package/devices/philips.js +3118 -0
- package/devices/plaid.js +25 -0
- package/devices/plugwise.js +173 -0
- package/devices/popp.js +10 -0
- package/devices/profalux.js +47 -0
- package/devices/prolight.js +54 -0
- package/devices/qmotion.js +33 -0
- package/devices/qoto.js +113 -0
- package/devices/quotra.js +18 -0
- package/devices/rademacher.js +18 -0
- package/devices/rgb_genie.js +119 -0
- package/devices/robb.js +335 -0
- package/devices/roome.js +15 -0
- package/devices/rtx.js +90 -0
- package/devices/salus_controls.js +137 -0
- package/devices/samotech.js +93 -0
- package/devices/saswell.js +58 -0
- package/devices/scanproducts.js +32 -0
- package/devices/schlage.js +24 -0
- package/devices/schneider_electric.js +1039 -0
- package/devices/schwaiger.js +62 -0
- package/devices/seastar_intelligence.js +16 -0
- package/devices/securifi.js +39 -0
- package/devices/sengled.js +332 -0
- package/devices/sercomm.js +140 -0
- package/devices/shenzhen_homa.js +53 -0
- package/devices/shinasystem.js +686 -0
- package/devices/siglis.js +440 -0
- package/devices/sinope.js +1257 -0
- package/devices/siterwell.js +46 -0
- package/devices/skydance.js +112 -0
- package/devices/slv.js +27 -0
- package/devices/smart9.js +27 -0
- package/devices/smart_home_pty.js +18 -0
- package/devices/smartenit.js +42 -0
- package/devices/smartthings.js +495 -0
- package/devices/smartwings.js +24 -0
- package/devices/sohan_electric.js +13 -0
- package/devices/solaredge.js +11 -0
- package/devices/somgoms.js +47 -0
- package/devices/sonoff.js +262 -0
- package/devices/spotmau.js +36 -0
- package/devices/sprut.js +317 -0
- package/devices/stelpro.js +216 -0
- package/devices/sunricher.js +625 -0
- package/devices/swann.js +33 -0
- package/devices/sylvania.js +200 -0
- package/devices/tci.js +25 -0
- package/devices/technicolor.js +47 -0
- package/devices/terncy.js +64 -0
- package/devices/the_light_group.js +70 -0
- package/devices/third_reality.js +195 -0
- package/devices/titan_products.js +22 -0
- package/devices/tplink.js +42 -0
- package/devices/trust.js +114 -0
- package/devices/tubeszb.js +20 -0
- package/devices/tuya.js +4215 -0
- package/devices/ubisys.js +938 -0
- package/devices/uhome.js +25 -0
- package/devices/universal_electronics_inc.js +119 -0
- package/devices/urlighting.js +12 -0
- package/devices/useelink.js +52 -0
- package/devices/vbled.js +18 -0
- package/devices/vesternet.js +188 -0
- package/devices/viessmann.js +51 -0
- package/devices/villeroy_boch.js +18 -0
- package/devices/vimar.js +78 -0
- package/devices/visonic.js +80 -0
- package/devices/vrey.js +18 -0
- package/devices/wally.js +25 -0
- package/devices/waxman.js +45 -0
- package/devices/weiser.js +67 -0
- package/devices/weten.js +13 -0
- package/devices/wisdom.js +11 -0
- package/devices/woox.js +177 -0
- package/devices/wyze.js +23 -0
- package/devices/xiaomi.js +3223 -0
- package/devices/xinghuoyuan.js +11 -0
- package/devices/yale.js +227 -0
- package/devices/ynoa.js +68 -0
- package/devices/yookee.js +23 -0
- package/devices/ysrsai.js +26 -0
- package/devices/zemismart.js +254 -0
- package/devices/zen.js +34 -0
- package/devices/zipato.js +11 -0
- package/index.js +242 -0
- package/lib/color.js +784 -0
- package/lib/configureKey.js +944 -0
- package/lib/constants.js +316 -0
- package/lib/exposes.js +677 -0
- package/lib/extend.js +180 -0
- package/lib/kelvinToXy.js +1912 -0
- package/lib/legacy.js +2223 -0
- package/lib/light.js +111 -0
- package/lib/ota/OTA_URLs.md +119 -0
- package/lib/ota/common.js +476 -0
- package/lib/ota/index.js +10 -0
- package/lib/ota/inovelli.js +72 -0
- package/lib/ota/ledvance.js +52 -0
- package/lib/ota/lixee.js +57 -0
- package/lib/ota/salus.js +82 -0
- package/lib/ota/securifi.js +25 -0
- package/lib/ota/tradfri.js +45 -0
- package/lib/ota/ubisys.js +61 -0
- package/lib/ota/zigbeeOTA.js +161 -0
- package/lib/philips.js +667 -0
- package/lib/reporting.js +234 -0
- package/lib/store.js +57 -0
- package/lib/tuya.js +2027 -0
- package/lib/utils.js +527 -0
- package/lib/xiaomi.d.ts +11 -0
- package/lib/xiaomi.js +1288 -0
- package/lib/zosung.js +243 -0
- package/package.json +39 -0
package/lib/ota/salus.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const url = 'https://eu.salusconnect.io/demo/default/status/firmware?timestamp=0';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const common = require('./common');
|
|
4
|
+
const tar = require('tar-stream');
|
|
5
|
+
const axios = common.getAxios();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper functions
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
async function getImageMeta(current, logger, device) {
|
|
12
|
+
const modelID = device.modelID;
|
|
13
|
+
const images = (await axios.get(url)).data.versions;
|
|
14
|
+
const image = images.find((i) => i.model === modelID);
|
|
15
|
+
assert(image, `No image available for modelID '${modelID}'`);
|
|
16
|
+
return {
|
|
17
|
+
fileVersion: parseInt(image.version, 16),
|
|
18
|
+
url: image.url.replace(/^http:\/\//, 'https://'),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function untar(tarStream) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const extract = tar.extract();
|
|
25
|
+
|
|
26
|
+
const result = [];
|
|
27
|
+
|
|
28
|
+
extract.on('error', reject);
|
|
29
|
+
|
|
30
|
+
extract.on('entry', (headers, stream, next) => {
|
|
31
|
+
const buffers = [];
|
|
32
|
+
|
|
33
|
+
stream.on('data', function(data) {
|
|
34
|
+
buffers.push(data);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
stream.on('end', function() {
|
|
38
|
+
result.push({
|
|
39
|
+
headers,
|
|
40
|
+
data: Buffer.concat(buffers),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
next();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
stream.resume();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
extract.on('finish', () => {
|
|
50
|
+
resolve(result);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
tarStream.pipe(extract);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function downloadImage(meta, logger) {
|
|
58
|
+
const download = await axios.get(meta.url, {responseType: 'stream'});
|
|
59
|
+
|
|
60
|
+
const files = await untar(download.data);
|
|
61
|
+
|
|
62
|
+
const imageFile = files.find((file) => file.headers.name.endsWith('.ota'));
|
|
63
|
+
|
|
64
|
+
return imageFile;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Interface implementation
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
async function isUpdateAvailable(device, logger, requestPayload=null) {
|
|
72
|
+
return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function updateToLatest(device, logger, onProgress) {
|
|
76
|
+
return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta, downloadImage);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
isUpdateAvailable,
|
|
81
|
+
updateToLatest,
|
|
82
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const common = require('./common');
|
|
2
|
+
const zigbeeOTA = require('./zigbeeOTA');
|
|
3
|
+
|
|
4
|
+
async function isUpdateAvailable(device, logger, requestPayload=null) {
|
|
5
|
+
if (device.modelID === 'PP-WHT-US') {
|
|
6
|
+
// see https://github.com/Koenkk/zigbee-OTA/pull/14
|
|
7
|
+
const scenesEndpoint = device.endpoints.find((e) => e.supportsOutputCluster('genScenes'));
|
|
8
|
+
await scenesEndpoint.write('genScenes', {currentGroup: 49502});
|
|
9
|
+
}
|
|
10
|
+
return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, zigbeeOTA.getImageMeta);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function updateToLatest(device, logger, onProgress) {
|
|
14
|
+
if (device.modelID === 'PP-WHT-US') {
|
|
15
|
+
// see https://github.com/Koenkk/zigbee-OTA/pull/14
|
|
16
|
+
const scenesEndpoint = device.endpoints.find((e) => e.supportsOutputCluster('genScenes'));
|
|
17
|
+
await scenesEndpoint.write('genScenes', {currentGroup: 49502});
|
|
18
|
+
}
|
|
19
|
+
return common.updateToLatest(device, logger, onProgress, common.getNewImage, zigbeeOTA.getImageMeta);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
isUpdateAvailable,
|
|
24
|
+
updateToLatest,
|
|
25
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const productionURL = 'http://fw.ota.homesmart.ikea.net/feed/version_info.json';
|
|
2
|
+
const testURL = 'http://fw.test.ota.homesmart.ikea.net/feed/version_info.json';
|
|
3
|
+
const common = require('./common');
|
|
4
|
+
const axios = common.getAxios();
|
|
5
|
+
let useTestURL = false;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper functions
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
async function getImageMeta(current, logger, device) {
|
|
12
|
+
const url = useTestURL ? testURL : productionURL;
|
|
13
|
+
const imageType = current.imageType;
|
|
14
|
+
const images = (await axios.get(url)).data;
|
|
15
|
+
const image = images.find((i) => i.fw_image_type === imageType);
|
|
16
|
+
|
|
17
|
+
if (!image) {
|
|
18
|
+
throw new Error(`No image available for imageType '${imageType}'`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
fileVersion: (image.fw_file_version_MSB << 16) | image.fw_file_version_LSB,
|
|
23
|
+
url: image.fw_binary_url,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Interface implementation
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
async function isUpdateAvailable(device, logger, requestPayload=null) {
|
|
32
|
+
return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function updateToLatest(device, logger, onProgress) {
|
|
36
|
+
return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
isUpdateAvailable,
|
|
41
|
+
updateToLatest,
|
|
42
|
+
useTestURL: () => {
|
|
43
|
+
useTestURL = true;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const firmwareHtmlPageUrl = 'http://fwu.ubisys.de/smarthome/OTA/release/index';
|
|
2
|
+
const imageRegex = /10F2-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{8})\S*ota1?\.zigbee/gi;
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const url = require('url');
|
|
5
|
+
const common = require('./common');
|
|
6
|
+
const axios = common.getAxios();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper functions
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
async function getImageMeta(current, logger, device) {
|
|
13
|
+
const imageType = current.imageType;
|
|
14
|
+
const hardwareVersion = device.hardwareVersion;
|
|
15
|
+
|
|
16
|
+
const firmwarePage = await axios.get(firmwareHtmlPageUrl);
|
|
17
|
+
logger.debug(
|
|
18
|
+
`OTA ubisys: got firmware page, status: ${firmwarePage.status}, data.length: ${firmwarePage.data.length}`);
|
|
19
|
+
assert(firmwarePage.status === 200, `HTTP Error getting ubisys firmware page`);
|
|
20
|
+
const firmwarePageHtml = firmwarePage.data;
|
|
21
|
+
|
|
22
|
+
imageRegex.lastIndex = 0; // reset (global) regex for next exec to match from the beginning again
|
|
23
|
+
let imageMatch = imageRegex.exec(firmwarePageHtml);
|
|
24
|
+
let highestMatch = null;
|
|
25
|
+
while (imageMatch != null) {
|
|
26
|
+
logger.debug(`OTA ubisys: image found: ${imageMatch[0]}`);
|
|
27
|
+
if (parseInt(imageMatch[1], 16) === imageType &&
|
|
28
|
+
parseInt(imageMatch[2], 16) <= hardwareVersion && hardwareVersion <= parseInt(imageMatch[3], 16)) {
|
|
29
|
+
if (highestMatch === null || parseInt(highestMatch[4], 16) < parseInt(imageMatch[4], 16)) {
|
|
30
|
+
highestMatch = imageMatch;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
imageMatch = imageRegex.exec(firmwarePageHtml);
|
|
34
|
+
}
|
|
35
|
+
assert(highestMatch !== null,
|
|
36
|
+
`No image available for imageType '0x${imageType.toString(16)}' with hardware version ${hardwareVersion}`);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
hardwareVersionMin: parseInt(highestMatch[2], 16),
|
|
40
|
+
hardwareVersionMax: parseInt(highestMatch[3], 16),
|
|
41
|
+
fileVersion: parseInt(highestMatch[4], 16),
|
|
42
|
+
url: url.resolve(firmwareHtmlPageUrl, highestMatch[0]),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Interface implementation
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
async function isUpdateAvailable(device, logger, requestPayload=null) {
|
|
51
|
+
return common.isUpdateAvailable(device, logger, common.isNewImageAvailable, requestPayload, getImageMeta);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function updateToLatest(device, logger, onProgress) {
|
|
55
|
+
return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
isUpdateAvailable,
|
|
60
|
+
updateToLatest,
|
|
61
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const url = 'https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/index.json';
|
|
2
|
+
const common = require('./common');
|
|
3
|
+
const axios = common.getAxios();
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const URI = require('uri-js');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
let overrideIndexFileName = null;
|
|
9
|
+
let dataDir = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Helper functions
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
function isValidUrl(url) {
|
|
17
|
+
let parsed;
|
|
18
|
+
try {
|
|
19
|
+
parsed = URI.parse(url);
|
|
20
|
+
} catch (_) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return parsed.scheme === 'http' || parsed.scheme === 'https';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function getIndexFile(urlOrName) {
|
|
27
|
+
if (isValidUrl(urlOrName)) {
|
|
28
|
+
return (await axios.get(urlOrName)).data;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return JSON.parse(fs.readFileSync(urlOrName));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readLocalFile(fileName, logger) {
|
|
35
|
+
// If the file name is not a full path, then treat it as a relative to the data directory
|
|
36
|
+
if (!path.isAbsolute(fileName) && dataDir) {
|
|
37
|
+
fileName = path.join(dataDir, fileName);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
logger.debug(`ZigbeeOTA: getting local firmware file ${fileName}`);
|
|
41
|
+
return fs.readFileSync(fileName);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function getFirmwareFile(image, logger) {
|
|
45
|
+
const urlOrName = image.url;
|
|
46
|
+
|
|
47
|
+
// First try to download firmware file with the URL provided
|
|
48
|
+
if (isValidUrl(urlOrName)) {
|
|
49
|
+
logger.debug(`ZigbeeOTA: downloading firmware image from ${urlOrName}`);
|
|
50
|
+
return await axios.get(urlOrName, {responseType: 'arraybuffer'});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {data: readLocalFile(urlOrName, logger)};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fillImageInfo(meta, logger) {
|
|
57
|
+
// Web-hosted images must come with all fields filled already
|
|
58
|
+
if (isValidUrl(meta.url)) {
|
|
59
|
+
return meta;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Nothing to do if needed fields were filled already
|
|
63
|
+
if (meta.hasOwnProperty('imageType') &&
|
|
64
|
+
meta.hasOwnProperty('manufacturerCode') &&
|
|
65
|
+
meta.hasOwnProperty('fileVersion')) {
|
|
66
|
+
return meta;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// If no fields provided - get them from the image file
|
|
70
|
+
const buffer = readLocalFile(meta.url, logger);
|
|
71
|
+
const start = buffer.indexOf(common.upgradeFileIdentifier);
|
|
72
|
+
const image = common.parseImage(buffer.slice(start));
|
|
73
|
+
|
|
74
|
+
// Will fill only those fields that were absent
|
|
75
|
+
if (!meta.hasOwnProperty('imageType')) meta.imageType = image.header.imageType;
|
|
76
|
+
if (!meta.hasOwnProperty('manufacturerCode')) meta.manufacturerCode = image.header.manufacturerCode;
|
|
77
|
+
if (!meta.hasOwnProperty('fileVersion')) meta.fileVersion = image.header.fileVersion;
|
|
78
|
+
return meta;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function getIndex(logger) {
|
|
82
|
+
const index = (await axios.get(url)).data;
|
|
83
|
+
|
|
84
|
+
logger.debug(`ZigbeeOTA: downloaded main index`);
|
|
85
|
+
|
|
86
|
+
if (overrideIndexFileName) {
|
|
87
|
+
logger.debug(`ZigbeeOTA: Loading override index ${overrideIndexFileName}`);
|
|
88
|
+
const localIndex = await getIndexFile(overrideIndexFileName);
|
|
89
|
+
|
|
90
|
+
// Resulting index will have overriden items first
|
|
91
|
+
return localIndex.concat(index).map((item) => isValidUrl(item.url) ? item : fillImageInfo(item, logger));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return index;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function getImageMeta(current, logger, device) {
|
|
98
|
+
const modelId = device.modelID;
|
|
99
|
+
const imageType = current.imageType;
|
|
100
|
+
const manufacturerCode = current.manufacturerCode;
|
|
101
|
+
const manufacturerName = device.manufacturerName;
|
|
102
|
+
const images = await getIndex(logger);
|
|
103
|
+
|
|
104
|
+
// NOTE: Officially an image can be determined with a combination of manufacturerCode and imageType.
|
|
105
|
+
// However Gledopto pro products use the same imageType (0) for every device while the image is different.
|
|
106
|
+
// For this case additional identification through the modelId is done.
|
|
107
|
+
// In the case of Tuya and Moes, additional identification is carried out through the manufacturerName.
|
|
108
|
+
const image = images.find((i) => i.imageType === imageType && i.manufacturerCode === manufacturerCode &&
|
|
109
|
+
(!i.minFileVersion || current.fileVersion >= i.minFileVersion) && (!i.maxFileVersion || current.fileVersion <= i.maxFileVersion) &&
|
|
110
|
+
(!i.modelId || i.modelId === modelId) && (!i.manufacturerName || i.manufacturerName.includes(manufacturerName)));
|
|
111
|
+
|
|
112
|
+
if (!image) {
|
|
113
|
+
throw new Error(`No image available for imageType '${imageType}'`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
fileVersion: image.fileVersion,
|
|
118
|
+
fileSize: image.fileSize,
|
|
119
|
+
url: image.url,
|
|
120
|
+
sha512: image.sha512,
|
|
121
|
+
force: image.force,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function isNewImageAvailable(current, logger, device, getImageMeta) {
|
|
126
|
+
if (device.modelID === 'lumi.airrtc.agl001') {
|
|
127
|
+
// The current.fileVersion which comes from the device is wrong.
|
|
128
|
+
// Use the `aqaraFileVersion` which comes from the aqaraOpple.attributeReport instead.
|
|
129
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/16345#issuecomment-1454835056
|
|
130
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/16345 doesn't seem to be needed for all
|
|
131
|
+
if (device.meta.aqaraFileVersion) {
|
|
132
|
+
current = {...current, fileVersion: device.meta.aqaraFileVersion};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return common.isNewImageAvailable(current, logger, device, getImageMeta);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Interface implementation
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
async function isUpdateAvailable(device, logger, requestPayload=null) {
|
|
144
|
+
return common.isUpdateAvailable(device, logger, isNewImageAvailable, requestPayload, getImageMeta);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function updateToLatest(device, logger, onProgress) {
|
|
148
|
+
return common.updateToLatest(device, logger, onProgress, common.getNewImage, getImageMeta, getFirmwareFile);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
getImageMeta,
|
|
153
|
+
isUpdateAvailable,
|
|
154
|
+
updateToLatest,
|
|
155
|
+
useIndexOverride: (indexFileName) => {
|
|
156
|
+
overrideIndexFileName = indexFileName;
|
|
157
|
+
},
|
|
158
|
+
setDataDir: (dir) => {
|
|
159
|
+
dataDir = dir;
|
|
160
|
+
},
|
|
161
|
+
};
|