+
Matterbridge Dashboard
+
+
Loading Matterbridge status...
+
Loading Matterbridge current version...
+
Loading Matterbridge latest version...
+
Loading Shelly plugin current version...
+
Loading Shelly plugin latest version...
+
System logs:
+
Fetching logs...
+
+
+
+
\ No newline at end of file
diff --git a/rock-s0/cockpit/manifest.json b/rock-s0/cockpit/manifest.json
new file mode 100644
index 0000000..c8b1545
--- /dev/null
+++ b/rock-s0/cockpit/manifest.json
@@ -0,0 +1,11 @@
+{
+ "name": "matterbridge",
+ "title": "Matterbridge Management",
+ "version": "1.0",
+ "menu": {
+ "index": {
+ "label": "Matterbridge"
+ }
+ },
+ "content-security-policy": "default-src 'self'; script-src 'self'; style-src 'self';"
+}
\ No newline at end of file
diff --git a/rock-s0/cockpit/matterbridge.css b/rock-s0/cockpit/matterbridge.css
new file mode 100644
index 0000000..e67bb56
--- /dev/null
+++ b/rock-s0/cockpit/matterbridge.css
@@ -0,0 +1,18 @@
+#content {
+ font-family: Arial, sans-serif;
+ padding: 20px;
+}
+
+button {
+ padding: 5px 10px;
+ margin: 10px 0;
+}
+
+#status,
+#matterbridge-current,
+#matterbridge-latest,
+#shelly-current,
+#shelly-latest,
+#logs {
+ margin: 10px 0;
+}
\ No newline at end of file
diff --git a/rock-s0/cockpit/matterbridge.js b/rock-s0/cockpit/matterbridge.js
new file mode 100644
index 0000000..7396280
--- /dev/null
+++ b/rock-s0/cockpit/matterbridge.js
@@ -0,0 +1,109 @@
+/* eslint-disable no-control-regex */
+/* eslint-disable no-console */
+// Wait for Cockpit to fully initialize
+cockpit.transport.wait(function () {
+ console.log('Matterbridge Cockpit extension loaded');
+
+ // Fetch and display the Matterbridge status
+ function fetchStatus() {
+ cockpit
+ .spawn(['systemctl', 'is-active', 'matterbridge'])
+ .then(function (status) {
+ document.getElementById('status').innerText = `Status: ${status.trim().replace('\n', '')}`;
+ })
+ .catch(function (error) {
+ console.error('Error fetching Matterbridge status:', error);
+ document.getElementById('status').innerText = 'Error fetching status.';
+ });
+ }
+
+ // Fetch and display the Matterbridge current version
+ function fetchMatterbridgeCurrent() {
+ cockpit
+ .spawn(['npm', 'list', '-g', 'matterbridge'])
+ .then(function (status) {
+ // Extract the version number using a regular expression
+ const versionMatch = status.match(/matterbridge@(\d+\.\d+\.\d+)/);
+ const version = versionMatch ? versionMatch[1] : 'Unknown';
+ document.getElementById('matterbridge-current').innerText = `Current version: ${version}`;
+ })
+ .catch(function (error) {
+ console.error('Error fetching Matterbridge current version:', error);
+ document.getElementById('matterbridge-current').innerText = 'Error fetching Matterbridge current version.';
+ });
+ }
+
+ // Fetch and display the Matterbridge latest version
+ function fetchMatterbridgeLatest() {
+ cockpit
+ .spawn(['npm', 'show', 'matterbridge', 'version'])
+ // cockpit.spawn(["whoami"])
+ .then(function (status) {
+ document.getElementById('matterbridge-latest').innerText = `Latest version: ${status.trim()}`;
+ })
+ .catch(function (error) {
+ console.error('Error fetching Matterbridge latest version:', error);
+ document.getElementById('matterbridge-latest').innerText = 'Error fetching Matterbridge latest version.';
+ });
+ }
+
+ // Fetch and display the Shelly plugin current version
+ function fetchShellyCurrent() {
+ cockpit
+ .spawn(['npm', 'list', '-g', 'matterbridge-shelly'])
+ .then(function (status) {
+ // Extract the version number using a regular expression
+ const versionMatch = status.match(/matterbridge-shelly@(\d+\.\d+\.\d+)/);
+ const version = versionMatch ? versionMatch[1] : 'Unknown';
+ document.getElementById('shelly-current').innerText = `Shelly plugin current version: ${version}`;
+ })
+ .catch(function (error) {
+ console.error('Error fetching Shelly plugin current version:', error);
+ document.getElementById('shelly-current').innerText = 'Error fetching Shelly plugin current version.';
+ });
+ }
+
+ // Fetch and display the Shelly plugin latest version
+ function fetchShellyLatest() {
+ cockpit
+ .spawn(['npm', 'show', 'matterbridge-shelly', 'version'])
+ // cockpit.spawn(["whoami"])
+ .then(function (status) {
+ document.getElementById('shelly-latest').innerText = `Shelly plugin latest version: ${status.trim()}`;
+ })
+ .catch(function (error) {
+ console.error('Error fetching Shelly plugin latest version:', error);
+ document.getElementById('shelly-latest').innerText = 'Error fetching Shelly plugin latest version.';
+ });
+ }
+
+ // Fetch logs
+ function fetchLogs() {
+ cockpit
+ .spawn(['journalctl', '-u', 'matterbridge', '--no-pager', '-n', '20', '-o', 'cat'])
+ .then(function (logs) {
+ // const filteredLogs = logs.split('\n').filter(line => !line.includes('matterbridge.service')).join('\n');
+ logs = logs.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
+ document.getElementById('logs').innerText = logs;
+ })
+ .catch(function (error) {
+ console.error('Error fetching logs:', error);
+ document.getElementById('logs').innerText = 'Error fetching logs.';
+ });
+ }
+
+ // Reload the Matterbridge configuration
+ document.getElementById('frontend-button').addEventListener('click', function () {
+ const hostname = window.location.hostname;
+ const newUrl = `http://${hostname}:8283`;
+ window.open(newUrl, '_blank');
+ });
+
+ // Initial fetch of status and logs
+ fetchStatus();
+ fetchMatterbridgeCurrent();
+ fetchMatterbridgeLatest();
+ fetchShellyCurrent();
+ fetchShellyLatest();
+ fetchLogs();
+});
diff --git a/rock-s0/systemd/matterbridge.service b/rock-s0/systemd/matterbridge.service
index 7dd4be4..bac4f76 100644
--- a/rock-s0/systemd/matterbridge.service
+++ b/rock-s0/systemd/matterbridge.service
@@ -4,7 +4,7 @@ After=network-online.target
[Service]
Type=simple
-ExecStart=matterbridge -service -passcode 20242025 -discriminator 3840
+ExecStart=matterbridge -service -passcode 20242025 -discriminator 3840 -mdnsinterface end0
WorkingDirectory=/home/rock/Matterbridge
StandardOutput=inherit
StandardError=inherit
@@ -12,6 +12,7 @@ Restart=always
RestartSec=10s
TimeoutStopSec=30s
User=rock
+Group=rock
[Install]
WantedBy=multi-user.target
diff --git a/src/coapServer.ts b/src/coapServer.ts
index 697d558..158d00f 100644
--- a/src/coapServer.ts
+++ b/src/coapServer.ts
@@ -416,6 +416,11 @@ export class CoapServer extends EventEmitter {
if (s.D === 'inputEventCnt' && b.D.startsWith('sensor'))
desc.push({ id: s.I, component: b.D.replace('_', ':').replace('sensor', 'input'), property: 'event_cnt', range: s.R }); // SHBTN-2
+ // ht component
+ if (s.D === 'extTemp' && s.U === 'C' && b.D.startsWith('sensor')) desc.push({ id: s.I, component: 'temperature', property: 'tC', range: s.R }); // SHHT-1
+ if (s.D === 'extTemp' && s.U === 'F' && b.D.startsWith('sensor')) desc.push({ id: s.I, component: 'temperature', property: 'tF', range: s.R }); // SHHT-1
+ if (s.D === 'humidity' && b.D.startsWith('sensor')) desc.push({ id: s.I, component: 'humidity', property: 'value', range: s.R }); // SHHT-1
+
// gas_sensor component
if (s.D === 'sensorOp' && b.D.startsWith('sensor')) desc.push({ id: s.I, component: 'gas', property: 'sensor_state', range: s.R }); // SHGS-1
if (s.D === 'gas' && b.D.startsWith('sensor')) desc.push({ id: s.I, component: 'gas', property: 'alarm_state', range: s.R }); // SHGS-1
diff --git a/src/mdnsScanner.test.ts b/src/mdnsScanner.test.ts
index 489a085..bc3b5d4 100644
--- a/src/mdnsScanner.test.ts
+++ b/src/mdnsScanner.test.ts
@@ -175,6 +175,25 @@ describe('Shellies MdnsScanner test', () => {
expect(mdns.isScanning).toBeFalsy();
}, 10000);
+ test('Shelly ht', (done) => {
+ discoveredDeviceListener.mockClear();
+ mdns.on('discovered', discoveredDeviceListener);
+ mdns.start(undefined, undefined, undefined, true);
+ expect(mdns.isScanning).toBeTruthy();
+ setTimeout(() => {
+ expect(discoveredDeviceListener).toHaveBeenCalledWith({ id: 'shellyht-CA969F', host: '192.168.68.173', port: 80, gen: 1 });
+ expect((mdns as any).discoveredDevices.has('shellyht-CA969F')).toBeTruthy();
+ done();
+ }, 1000);
+ (mdns as any).discoveredDevices.clear();
+ const data = loadResponse('shellyht-CA969F');
+ expect(data).not.toBeUndefined();
+ if (!data) return;
+ (mdns as any).scanner.emit('response', data, { address: '192.168.68.173', family: 'IPv4', port: 5353, size: 501 });
+ mdns.stop();
+ expect(mdns.isScanning).toBeFalsy();
+ }, 10000);
+
test('Shelly bulbduo', (done) => {
discoveredDeviceListener.mockClear();
mdns.on('discovered', discoveredDeviceListener);
diff --git a/src/mock/SHHT-1-CA969F.coap.citd.json b/src/mock/SHHT-1-CA969F.coap.citd.json
new file mode 100644
index 0000000..85ec501
--- /dev/null
+++ b/src/mock/SHHT-1-CA969F.coap.citd.json
@@ -0,0 +1,65 @@
+{
+ "blk": [
+ {
+ "I": 1,
+ "D": "sensor_0"
+ },
+ {
+ "I": 2,
+ "D": "device"
+ }
+ ],
+ "sen": [
+ {
+ "I": 9103,
+ "T": "EVC",
+ "D": "cfgChanged",
+ "R": "U16",
+ "L": 2
+ },
+ {
+ "I": 3101,
+ "T": "T",
+ "D": "extTemp",
+ "U": "C",
+ "R": ["-55/125", "999"],
+ "L": 1
+ },
+ {
+ "I": 3102,
+ "T": "T",
+ "D": "extTemp",
+ "U": "F",
+ "R": ["-67/257", "999"],
+ "L": 1
+ },
+ {
+ "I": 3103,
+ "T": "H",
+ "D": "humidity",
+ "R": ["0/100", "999"],
+ "L": 1
+ },
+ {
+ "I": 3115,
+ "T": "S",
+ "D": "sensorError",
+ "R": "0/1",
+ "L": 1
+ },
+ {
+ "I": 3111,
+ "T": "B",
+ "D": "battery",
+ "R": ["0/100", "-1"],
+ "L": 2
+ },
+ {
+ "I": 9102,
+ "T": "EV",
+ "D": "wakeupEvent",
+ "R": ["battery/button/periodic/poweron/sensor/alarm", "unknown"],
+ "L": 2
+ }
+ ]
+}
diff --git a/src/mock/SHHT-1-CA969F.coap.cits.json b/src/mock/SHHT-1-CA969F.coap.cits.json
new file mode 100644
index 0000000..f42f2de
--- /dev/null
+++ b/src/mock/SHHT-1-CA969F.coap.cits.json
@@ -0,0 +1,11 @@
+{
+ "G": [
+ [0, 9103, 0],
+ [0, 3101, 26.25],
+ [0, 3102, 79.25],
+ [0, 3103, 55.5],
+ [0, 3115, 0],
+ [0, 3111, 100],
+ [0, 9102, ["button"]]
+ ]
+}
diff --git a/src/mock/shellyblugw-B0B21CFAAD18.json b/src/mock/shellyblugw-B0B21CFAAD18.json
index 4c29a1e..c947788 100644
--- a/src/mock/shellyblugw-B0B21CFAAD18.json
+++ b/src/mock/shellyblugw-B0B21CFAAD18.json
@@ -154,4 +154,4 @@
"cfg_rev": 10,
"offset": 0,
"total": 0
-}
\ No newline at end of file
+}
diff --git a/src/mock/shellyblugw-B0B21CFAAD18.mdns.json b/src/mock/shellyblugw-B0B21CFAAD18.mdns.json
index f15c75c..9fe8489 100644
--- a/src/mock/shellyblugw-B0B21CFAAD18.mdns.json
+++ b/src/mock/shellyblugw-B0B21CFAAD18.mdns.json
@@ -68,9 +68,7 @@
"ttl": 120,
"class": "IN",
"flush": false,
- "data": [
- "gen=2"
- ]
+ "data": ["gen=2"]
},
{
"name": "ShellyBluGw-B0B21CFAAD18.local",
@@ -99,11 +97,7 @@
"ttl": 120,
"class": "IN",
"flush": false,
- "data": [
- "gen=2",
- "app=BluGw",
- "ver=1.4.2"
- ]
+ "data": ["gen=2", "app=BluGw", "ver=1.4.2"]
},
{
"name": "ShellyBluGw-B0B21CFAAD18.local",
@@ -114,4 +108,4 @@
"data": "192.168.1.168"
}
]
-}
\ No newline at end of file
+}
diff --git a/src/mock/shellyht-CA969F.json b/src/mock/shellyht-CA969F.json
new file mode 100644
index 0000000..269bf9e
--- /dev/null
+++ b/src/mock/shellyht-CA969F.json
@@ -0,0 +1,167 @@
+{
+ "shelly": {
+ "type": "SHHT-1",
+ "mac": "485519CA969F",
+ "auth": false,
+ "fw": "20230913-112531/v1.14.0-gcb84623",
+ "discoverable": false,
+ "sleep_mode": true
+ },
+ "settings": {
+ "device": {
+ "type": "SHHT-1",
+ "mac": "485519CA969F",
+ "hostname": "shellyht-CA969F",
+ "sleep_mode": true
+ },
+ "wifi_ap": {
+ "enabled": false,
+ "ssid": "shellyht-CA969F",
+ "key": ""
+ },
+ "wifi_sta": {
+ "enabled": true,
+ "ssid": "Tamer Umniah",
+ "ipv4_method": "dhcp",
+ "ip": null,
+ "gw": null,
+ "mask": null,
+ "dns": null
+ },
+ "wifi_sta1": {
+ "enabled": false,
+ "ssid": null,
+ "ipv4_method": "dhcp",
+ "ip": null,
+ "gw": null,
+ "mask": null,
+ "dns": null
+ },
+ "mqtt": {
+ "enable": false,
+ "server": "192.168.33.3:1883",
+ "user": "",
+ "id": "shellyht-CA969F",
+ "reconnect_timeout_max": 60,
+ "reconnect_timeout_min": 2,
+ "clean_session": true,
+ "keep_alive": 60,
+ "max_qos": 0,
+ "retain": false,
+ "update_period": 30
+ },
+ "coiot": {
+ "enabled": true,
+ "update_period": 15,
+ "peer": ""
+ },
+ "sntp": {
+ "server": "time.google.com",
+ "enabled": true
+ },
+ "login": {
+ "enabled": false,
+ "unprotected": false,
+ "username": "admin"
+ },
+ "pin_code": "",
+ "name": "HT Gen1",
+ "fw": "20230913-112531/v1.14.0-gcb84623",
+ "pon_wifi_reset": false,
+ "discoverable": false,
+ "build_info": {
+ "build_id": "20230913-112531/v1.14.0-gcb84623",
+ "build_timestamp": "2023-09-13T11:25:31Z",
+ "build_version": "1.0"
+ },
+ "cloud": {
+ "enabled": true,
+ "connected": false
+ },
+ "timezone": null,
+ "lat": null,
+ "lng": null,
+ "tzautodetect": true,
+ "tz_utc_offset": 10800,
+ "tz_dst": false,
+ "tz_dst_auto": true,
+ "time": "",
+ "unixtime": 0,
+ "debug_enable": false,
+ "allow_cross_origin": false,
+ "actions": {
+ "active": false,
+ "names": ["report_url", "temp_over_url", "temp_under_url", "hum_over_url", "hum_under_url"]
+ },
+ "sensors": {
+ "temperature_threshold": 1,
+ "temperature_unit": "C",
+ "humidity_threshold": 5
+ },
+ "sleep_mode": {
+ "period": 12,
+ "unit": "h"
+ },
+ "external_power": 0,
+ "temperature_offset": 0,
+ "humidity_offset": 0
+ },
+ "status": {
+ "wifi_sta": {
+ "connected": true,
+ "ssid": "Tamer Umniah",
+ "ip": "192.168.68.173",
+ "rssi": -37
+ },
+ "cloud": {
+ "enabled": true,
+ "connected": false
+ },
+ "mqtt": {
+ "connected": false
+ },
+ "time": "",
+ "unixtime": 0,
+ "serial": 1,
+ "has_update": false,
+ "mac": "485519CA969F",
+ "cfg_changed_cnt": 0,
+ "actions_stats": {
+ "skipped": 0
+ },
+ "is_valid": true,
+ "tmp": {
+ "value": 31.5,
+ "units": "C",
+ "tC": 31.5,
+ "tF": 88.7,
+ "is_valid": true
+ },
+ "hum": {
+ "value": 54.5,
+ "is_valid": true
+ },
+ "bat": {
+ "value": 100,
+ "voltage": 2.96
+ },
+ "act_reasons": ["button"],
+ "connect_retries": 0,
+ "sensor_error": 0,
+ "update": {
+ "status": "unknown",
+ "has_update": false,
+ "new_version": "",
+ "old_version": "20230913-112531/v1.14.0-gcb84623"
+ },
+ "ram_total": 52384,
+ "ram_free": 40704,
+ "fs_size": 233681,
+ "fs_free": 156122,
+ "uptime": 2
+ },
+ "components": [],
+ "cfg_rev": 0,
+ "offset": 0,
+ "total": 0
+}
diff --git a/src/mock/shellyht-CA969F.mdns.json b/src/mock/shellyht-CA969F.mdns.json
new file mode 100644
index 0000000..06b613d
--- /dev/null
+++ b/src/mock/shellyht-CA969F.mdns.json
@@ -0,0 +1,76 @@
+{
+ "id": 0,
+ "type": "response",
+ "flags": 1152,
+ "flag_qr": true,
+ "opcode": "QUERY",
+ "flag_aa": true,
+ "flag_tc": false,
+ "flag_rd": false,
+ "flag_ra": true,
+ "flag_z": false,
+ "flag_ad": false,
+ "flag_cd": false,
+ "rcode": "NOERROR",
+ "questions": [],
+ "answers": [
+ {
+ "name": "_services._dns-sd._udp.local",
+ "type": "PTR",
+ "ttl": 4500,
+ "class": "IN",
+ "flush": false,
+ "data": "_http._tcp.local"
+ },
+ {
+ "name": "_http._tcp.local",
+ "type": "PTR",
+ "ttl": 4500,
+ "class": "IN",
+ "flush": false,
+ "data": "shellyht-CA969F._http._tcp.local"
+ },
+ {
+ "name": "shellyht-CA969F._http._tcp.local",
+ "type": "SRV",
+ "ttl": 120,
+ "class": "IN",
+ "flush": true,
+ "data": {
+ "priority": 0,
+ "weight": 0,
+ "port": 80,
+ "target": "shellyht-CA969F.local"
+ }
+ },
+ {
+ "name": "shellyht-CA969F._http._tcp.local",
+ "type": "TXT",
+ "ttl": 120,
+ "class": "IN",
+ "flush": true,
+ "data": ["id=shellyht-CA969F", "arch=esp8266", "app=ht-sensor", "fw_version=1.0", "fw_id=20230913-112531/v1.14.0-gcb84623", "discoverable=false"]
+ },
+ {
+ "name": "shellyht-CA969F.local",
+ "type": "A",
+ "ttl": 120,
+ "class": "IN",
+ "flush": true,
+ "data": "192.168.68.173"
+ },
+ {
+ "name": "shellyht-CA969F.local",
+ "type": "NSEC",
+ "ttl": 120,
+ "class": "IN",
+ "flush": true,
+ "data": {
+ "nextDomain": "shellyht-CA969F.local",
+ "rrtypes": ["A"]
+ }
+ }
+ ],
+ "authorities": [],
+ "additionals": []
+}
diff --git a/src/mock/shellywalldisplay-00082261E102.json b/src/mock/shellywalldisplay-00082261E102.json
index 6f84b24..d97a856 100644
--- a/src/mock/shellywalldisplay-00082261E102.json
+++ b/src/mock/shellywalldisplay-00082261E102.json
@@ -258,4 +258,4 @@
"cfg_rev": 0,
"offset": 0,
"total": 0
-}
\ No newline at end of file
+}
diff --git a/src/mock/shellywalldisplay-00082261E102.mdns.json b/src/mock/shellywalldisplay-00082261E102.mdns.json
index 52fc3df..3012a14 100644
--- a/src/mock/shellywalldisplay-00082261E102.mdns.json
+++ b/src/mock/shellywalldisplay-00082261E102.mdns.json
@@ -20,12 +20,7 @@
"ttl": 4500,
"class": "IN",
"flush": true,
- "data": [
- "app=WallDisplay",
- "gen=2",
- "ver=2.2.1",
- "discoverable=true"
- ]
+ "data": ["app=WallDisplay", "gen=2", "ver=2.2.1", "discoverable=true"]
},
{
"name": "_services._dns-sd._udp.local",
@@ -49,12 +44,7 @@
"ttl": 4500,
"class": "IN",
"flush": true,
- "data": [
- "app=WallDisplay",
- "gen=2",
- "ver=2.2.1",
- "discoverable=true"
- ]
+ "data": ["app=WallDisplay", "gen=2", "ver=2.2.1", "discoverable=true"]
},
{
"name": "_services._dns-sd._udp.local",
@@ -173,10 +163,7 @@
"flush": true,
"data": {
"nextDomain": "ShellyWallDisplay-00082261E102._shelly._tcp.local",
- "rrtypes": [
- "TXT",
- "SRV"
- ]
+ "rrtypes": ["TXT", "SRV"]
}
},
{
@@ -187,10 +174,7 @@
"flush": true,
"data": {
"nextDomain": "ShellyWallDisplay-00082261E102._http._tcp.local",
- "rrtypes": [
- "TXT",
- "SRV"
- ]
+ "rrtypes": ["TXT", "SRV"]
}
},
{
@@ -201,9 +185,7 @@
"flush": true,
"data": {
"nextDomain": "167.1.168.192.in-addr.arpa",
- "rrtypes": [
- "PTR"
- ]
+ "rrtypes": ["PTR"]
}
},
{
@@ -214,9 +196,7 @@
"flush": true,
"data": {
"nextDomain": "2.0.1.E.1.6.E.F.F.F.2.2.8.0.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.E.F.ip6.arpa",
- "rrtypes": [
- "PTR"
- ]
+ "rrtypes": ["PTR"]
}
},
{
@@ -227,9 +207,7 @@
"flush": true,
"data": {
"nextDomain": "2.0.1.E.1.6.E.F.F.F.2.2.8.0.2.0.6.4.7.0.9.3.9.4.8.F.B.C.8.7.D.F.ip6.arpa",
- "rrtypes": [
- "PTR"
- ]
+ "rrtypes": ["PTR"]
}
},
{
@@ -240,9 +218,7 @@
"flush": true,
"data": {
"nextDomain": "A.0.E.1.3.B.9.9.2.0.2.9.C.B.0.2.6.4.7.0.9.3.9.4.8.F.B.C.8.7.D.F.ip6.arpa",
- "rrtypes": [
- "PTR"
- ]
+ "rrtypes": ["PTR"]
}
},
{
@@ -253,11 +229,8 @@
"flush": true,
"data": {
"nextDomain": "ShellyWallDisplay-00082261E102.local",
- "rrtypes": [
- "A",
- "AAAA"
- ]
+ "rrtypes": ["A", "AAAA"]
}
}
]
-}
\ No newline at end of file
+}
diff --git a/src/mock/shellywalldisplay-00082261E102.thermostat.json b/src/mock/shellywalldisplay-00082261E102.thermostat.json
index 23ed7e7..088a1e9 100644
--- a/src/mock/shellywalldisplay-00082261E102.thermostat.json
+++ b/src/mock/shellywalldisplay-00082261E102.thermostat.json
@@ -280,4 +280,4 @@
"cfg_rev": 0,
"offset": 0,
"total": 0
-}
\ No newline at end of file
+}
diff --git a/src/platform.ts b/src/platform.ts
index debde1c..eed316e 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -845,8 +845,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
tempComponent.on('update', (component: string, property: string, value: ShellyDataType) => {
this.shellyUpdateHandler(mbDevice, device, component, property, value);
});
- }
- if (tempComponent?.hasProperty('tC') && isValidNumber(tempComponent.getValue('tC'), -100, 100)) {
+ } else if (tempComponent?.hasProperty('tC') && isValidNumber(tempComponent.getValue('tC'), -100, 100)) {
const child = mbDevice.addChildDeviceTypeWithClusterServer(key, [DeviceTypes.TEMPERATURE_SENSOR], []);
const matterTemp = Math.min(Math.max(Math.round((tempComponent.getValue('tC') as number) * 100), -10000), 10000);
child.addClusterServer(mbDevice.getDefaultTemperatureMeasurementClusterServer(matterTemp));
@@ -857,9 +856,18 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
}
} else if (component.name === 'Humidity' && config.exposeHumidity !== 'disabled') {
const humidityComponent = device.getComponent(key);
+ if (humidityComponent?.hasProperty('value') && isValidNumber(humidityComponent.getValue('value'), 0, 100)) {
+ const child = mbDevice.addChildDeviceTypeWithClusterServer(key, [DeviceTypes.HUMIDITY_SENSOR], []);
+ const matterHumidity = Math.min(Math.max(Math.round((humidityComponent.getValue('value') as number) * 100), 0), 10000);
+ child.addClusterServer(mbDevice.getDefaultRelativeHumidityMeasurementClusterServer(matterHumidity));
+ // Add event handler
+ humidityComponent.on('update', (component: string, property: string, value: ShellyDataType) => {
+ this.shellyUpdateHandler(mbDevice, device, component, property, value);
+ });
+ }
if (humidityComponent?.hasProperty('rh') && isValidNumber(humidityComponent.getValue('rh'), 0, 100)) {
const child = mbDevice.addChildDeviceTypeWithClusterServer(key, [DeviceTypes.HUMIDITY_SENSOR], []);
- const matterHumidity = Math.min(Math.max(Math.round((humidityComponent.getValue('rh') as number) * 100), -10000), 10000);
+ const matterHumidity = Math.min(Math.max(Math.round((humidityComponent.getValue('rh') as number) * 100), 0), 10000);
child.addClusterServer(mbDevice.getDefaultRelativeHumidityMeasurementClusterServer(matterHumidity));
// Add event handler
humidityComponent.on('update', (component: string, property: string, value: ShellyDataType) => {
@@ -1707,8 +1715,8 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
if (shellyComponent.name === 'Temperature' && (property === 'value' || property === 'tC') && isValidNumber(value, -100, +100)) {
matterbridgeDevice.setAttribute(TemperatureMeasurementCluster.id, 'measuredValue', value * 100, shellyDevice.log, endpoint);
}
- // Update for Humidity when has rh
- if (shellyComponent.name === 'Humidity' && property === 'rh' && isValidNumber(value, 0, 100)) {
+ // Update for Humidity when has value or rh
+ if (shellyComponent.name === 'Humidity' && (property === 'value' || property === 'rh') && isValidNumber(value, 0, 100)) {
matterbridgeDevice.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', value * 100, shellyDevice.log, endpoint);
}
// Update for Illuminance when has lux
diff --git a/src/shellyDevice.mock.test.ts b/src/shellyDevice.mock.test.ts
index 49b9ab5..06ac9ea 100644
--- a/src/shellyDevice.mock.test.ts
+++ b/src/shellyDevice.mock.test.ts
@@ -514,6 +514,51 @@ describe('Shelly devices test', () => {
if (device) device.destroy();
});
+ test('Create a gen 1 shellyht device', async () => {
+ id = 'shellyht-CA969F';
+ log.logName = id;
+
+ device = await ShellyDevice.create(shelly, log, path.join('src', 'mock', id + '.json'));
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ expect(device.host).toBe(path.join('src', 'mock', id + '.json'));
+ expect(device.model).toBe('SHHT-1');
+ expect(device.mac).toBe('485519CA969F');
+ expect(device.id).toBe(id);
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.profile).toBe(undefined);
+ expect(device.name).toBe('HT Gen1');
+ expect(device.hasUpdate).toBe(false);
+ expect(device.lastseen).not.toBe(0);
+ expect(device.online).toBe(true);
+ expect(device.cached).toBe(false);
+ expect(device.sleepMode).toBe(true);
+
+ expect(device.components.length).toBe(10);
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Temperature', 'Humidity', 'Battery']);
+ expect(device.getComponentIds()).toStrictEqual(['wifi_ap', 'wifi_sta', 'wifi_sta1', 'mqtt', 'coiot', 'sntp', 'cloud', 'temperature', 'humidity', 'battery']);
+
+ expect(device.getComponent('temperature')?.getValue('value')).toBe(31.5);
+ expect(device.getComponent('temperature')?.getValue('units')).toBe('C');
+ expect(device.getComponent('temperature')?.getValue('tC')).toBe(31.5);
+ expect(device.getComponent('temperature')?.getValue('tF')).toBe(88.7);
+ expect(device.getComponent('temperature')?.getValue('is_valid')).toBe(true);
+ expect(device.getComponent('humidity')?.getValue('value')).toBe(54.5);
+ expect(device.getComponent('humidity')?.getValue('is_valid')).toBe(true);
+ expect(device.getComponent('battery')?.getValue('level')).toBe(100);
+ expect(device.getComponent('battery')?.getValue('voltage')).toBe(2.96);
+ expect(device.getComponent('battery')?.getValue('charging')).toBe(undefined);
+
+ expect(device.getComponent('sys')?.getValue('temperature')).toBe(undefined);
+ expect(device.getComponent('sys')?.getValue('overtemperature')).toBe(undefined);
+
+ expect(await device.fetchUpdate()).not.toBeNull();
+
+ if (device) device.destroy();
+ });
+
test('Create a gen 1 shellygas device', async () => {
id = 'shellygas-7C87CEBCECE4';
log.logName = id;
diff --git a/src/shellyDevice.realgen1.test.ts b/src/shellyDevice.realgen1.test.ts
index 29b60da..8ffca06 100644
--- a/src/shellyDevice.realgen1.test.ts
+++ b/src/shellyDevice.realgen1.test.ts
@@ -16,7 +16,6 @@ describe('Shellies', () => {
let device: ShellyDevice | undefined;
const firmwareGen1 = 'v1.14.0-gcb84623';
- const firmwareGen2 = '1.4.2-gc2639da';
const address = '30:f6:ef:69:2b:c5';
beforeAll(async () => {
@@ -100,6 +99,54 @@ describe('Shellies', () => {
}, 20000);
});
+ describe('test real gen 1 shelly1l-E8DB84AAD781 241', () => {
+ if (getMacAddress() !== address) return;
+
+ test('Create a gen 1 shelly1 device and send commands', async () => {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.241');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.241');
+ expect(device.mac).toBe('E8DB84AAD781');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('SHSW-L');
+ expect(device.id).toBe('shelly1l-E8DB84AAD781');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('relay:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
+ });
+
describe('test real gen 1 shellydimmer2 119 with auth', () => {
if (getMacAddress() !== address) return;
@@ -264,4 +311,328 @@ describe('Shellies', () => {
device.destroy();
}, 30000);
});
+
+ describe('test real gen 1 shellyrgbw2-EC64C9D3FFEF mode color 226', () => {
+ if (getMacAddress() !== address) return;
+
+ test('Create a gen 1 shellyrgbw2 device and send commands', async () => {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.226');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.226');
+ expect(device.mac).toBe('EC64C9D3FFEF');
+ expect(device.profile).toBe('color');
+ expect(device.model).toBe('SHRGBW2');
+ expect(device.id).toBe('shellyrgbw2-EC64C9D3FFEF');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Light', 'Sys', 'PowerMeter', 'Input']);
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Level(40);
+ await waiter('Level(40)', () => { return component.getValue('brightness') === 40; }, true);
+
+ component.ColorRGB(255, 0, 0);
+ await waiter('ColorRGB(255, 0, 0)', () => { return component.getValue('red') === 255 && component.getValue('green') === 0 && component.getValue('blue') === 0; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Level(60);
+ await waiter('Level(60)', () => { return component.getValue('brightness') === 60; }, true);
+
+ component.ColorRGB(0, 255, 0);
+ await waiter('ColorRGB(0, 255, 0)', () => { return component.getValue('red') === 0 && component.getValue('green') === 255 && component.getValue('blue') === 0; }, true);
+
+ component.Off();
+ await waiter('Off 2', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+ });
+
+ describe('test real gen 1 shellyrgbw2-EC64C9D199AD mode white 152', () => {
+ if (getMacAddress() !== address) return;
+
+ test('Create a gen 1 shellyrgbw2 device and send commands', async () => {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.152');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.152');
+ expect(device.mac).toBe('EC64C9D199AD');
+ expect(device.profile).toBe('white');
+ expect(device.model).toBe('SHRGBW2');
+ expect(device.id).toBe('shellyrgbw2-EC64C9D199AD');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Light', 'Sys', 'PowerMeter', 'Input']);
+ expect(device.getComponentIds()).toStrictEqual([
+ 'wifi_ap',
+ 'wifi_sta',
+ 'wifi_sta1',
+ 'mqtt',
+ 'coiot',
+ 'sntp',
+ 'cloud',
+ 'light:0',
+ 'light:1',
+ 'light:2',
+ 'light:3',
+ 'sys',
+ 'meter:0',
+ 'meter:1',
+ 'meter:2',
+ 'meter:3',
+ 'input:0',
+ ]);
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isLightComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Level(40);
+ await waiter('Level(40)', () => { return component.getValue('brightness') === 40; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Level(60);
+ await waiter('Level(60)', () => { return component.getValue('brightness') === 60; }, true);
+
+ component.Off();
+ await waiter('Off 2', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 30000);
+ });
+
+ describe('test real gen 1 shellyem3-485519D732F4 249', () => {
+ if (getMacAddress() !== address) return;
+
+ test('Create a gen 1 shellyem3 device and send commands', async () => {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.249');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.249');
+ expect(device.mac).toBe('485519D732F4');
+ expect(device.profile).toBe(undefined);
+ expect(device.model).toBe('SHEM-3');
+ expect(device.id).toBe('shellyem3-485519D732F4');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ const component = device.getComponent('relay:0');
+ expect(component).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter('On', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+
+ component.Toggle();
+ await waiter('Toggle', () => { return component.getValue('state') === true; }, true);
+
+ component.Off();
+ await waiter('Off', () => { return component.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
+ });
+
+ describe('test real gen 1 shellyswitch25-3494547BF36C 236', () => {
+ if (getMacAddress() !== address) return;
+
+ test('Create a gen 1 shelly1 device and send commands', async () => {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.236');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.236');
+ expect(device.mac).toBe('3494547BF36C');
+ expect(device.profile).toBe('switch');
+ expect(device.model).toBe('SHSW-25');
+ expect(device.id).toBe('shellyswitch25-3494547BF36C');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Relay', 'PowerMeter', 'Input', 'Sys']);
+ expect(device.getComponentIds()).toStrictEqual([
+ 'wifi_ap',
+ 'wifi_sta',
+ 'wifi_sta1',
+ 'mqtt',
+ 'coiot',
+ 'sntp',
+ 'cloud',
+ 'relay:0',
+ 'relay:1',
+ 'meter:0',
+ 'meter:1',
+ 'input:0',
+ 'input:1',
+ 'sys',
+ ]);
+
+ const component0 = device.getComponent('relay:0');
+ expect(component0).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component0)) {
+ component0.On();
+ await waiter('On', () => { return component0.getValue('state') === true; }, true);
+
+ component0.Off();
+ await waiter('Off', () => { return component0.getValue('state') === false; }, true);
+
+ component0.Toggle();
+ await waiter('Toggle', () => { return component0.getValue('state') === true; }, true);
+
+ component0.Off();
+ await waiter('Off', () => { return component0.getValue('state') === false; }, true);
+ }
+
+ const component1 = device.getComponent('relay:1');
+ expect(component1).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isSwitchComponent(component1)) {
+ component1.On();
+ await waiter('On', () => { return component1.getValue('state') === true; }, true);
+
+ component1.Off();
+ await waiter('Off', () => { return component1.getValue('state') === false; }, true);
+
+ component1.Toggle();
+ await waiter('Toggle', () => { return component1.getValue('state') === true; }, true);
+
+ component1.Off();
+ await waiter('Off', () => { return component1.getValue('state') === false; }, true);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 20000);
+ });
+
+ describe('test real gen 1 shellyswitch25-3494546BBF7E 222', () => {
+ if (getMacAddress() !== address) return;
+
+ test('Create a gen 1 shelly1 device and send commands', async () => {
+ device = await ShellyDevice.create(shelly, log, '192.168.1.222');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ shelly.addDevice(device);
+
+ expect(device.host).toBe('192.168.1.222');
+ expect(device.mac).toBe('3494546BBF7E');
+ expect(device.profile).toBe('cover');
+ expect(device.model).toBe('SHSW-25');
+ expect(device.id).toBe('shellyswitch25-3494546BBF7E');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(false);
+ expect(device.gen).toBe(1);
+ expect(device.hasUpdate).toBe(false);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ expect(device.getComponentNames()).toStrictEqual(['WiFi', 'MQTT', 'CoIoT', 'Sntp', 'Cloud', 'Roller', 'PowerMeter', 'Input', 'Sys']);
+ expect(device.getComponentIds()).toStrictEqual(['wifi_ap', 'wifi_sta', 'wifi_sta1', 'mqtt', 'coiot', 'sntp', 'cloud', 'roller:0', 'meter:0', 'input:0', 'input:1', 'sys']);
+
+ const component0 = device.getComponent('roller:0');
+ expect(component0).not.toBeUndefined();
+
+ // prettier-ignore
+ if (isCoverComponent(component0)) {
+ component0.Open();
+ await waiter('Open', () => { return component0.getValue('state') === 'stop'; }, true, 30000);
+ await waiter('Open', () => { return component0.getValue('current_pos') === 100; }, true, 30000);
+
+ component0.Close();
+ await waiter('Close', () => { return component0.getValue('state') === 'stop'; }, true, 30000);
+ await waiter('Close', () => { return component0.getValue('current_pos') === 0; }, true, 30000);
+
+ component0.GoToPosition(50);
+ await waiter('GoToPosition(50)', () => { return component0.getValue('state') === 'stop'; }, true, 30000);
+ await waiter('GoToPosition(50)', () => { return component0.getValue('current_pos') === 50; }, true, 30000);
+
+ component0.Open();
+ await waiter('Open', () => { return component0.getValue('state') === 'stop'; }, true, 30000);
+ await waiter('Open', () => { return component0.getValue('current_pos') === 100; }, true, 30000);
+ }
+
+ shelly.removeDevice(device);
+ device.destroy();
+ }, 60000);
+ });
});
diff --git a/src/shellyDevice.ts b/src/shellyDevice.ts
index 6d91ce5..b52ebe9 100644
--- a/src/shellyDevice.ts
+++ b/src/shellyDevice.ts
@@ -21,7 +21,7 @@
* limitations under the License. *
*/
-import { AnsiLogger, LogLevel, BLUE, CYAN, GREEN, GREY, MAGENTA, RESET, db, debugStringify, er, hk, nf, wr, zb, rs, YELLOW, idn, nt } from 'matterbridge/logger';
+import { AnsiLogger, LogLevel, BLUE, CYAN, GREEN, GREY, MAGENTA, RESET, db, debugStringify, er, hk, nf, wr, zb, rs, YELLOW, idn, nt, rk } from 'matterbridge/logger';
import { getIpv4InterfaceAddress, isValidNumber, isValidObject, isValidString } from 'matterbridge/utils';
import { EventEmitter } from 'events';
import fetch, { RequestInit } from 'node-fetch';
@@ -186,16 +186,6 @@ export class ShellyDevice extends EventEmitter {
if (isCoverComponent(component)) return component as unknown as T;
return component as T;
}
- /*
- getComponent(id: string): ShellyComponent | ShellyLightComponent | ShellySwitchComponent | ShellyCoverComponent | undefined {
- const component = this._components.get(id);
- if (!component) return undefined;
- else if (component.isSwitchComponent()) return component as ShellySwitchComponent;
- else if (component.isLightComponent()) return component as ShellyLightComponent;
- else if (component.isCoverComponent()) return component as ShellyCoverComponent;
- else return component as ShellyComponent;
- }
- */
/**
* Retrieves an array of component IDs.
@@ -553,6 +543,7 @@ export class ShellyDevice extends EventEmitter {
if (key === 'tmp' && statusPayload.temperature === undefined && statusPayload.overtemperature === undefined) {
device.addComponent(new ShellyComponent(device, 'temperature', 'Temperature'));
}
+ if (key === 'hum') device.addComponent(new ShellyComponent(device, 'humidity', 'Humidity'));
if (key === 'voltage') device.addComponent(new ShellyComponent(device, 'sys', 'Sys'));
if (key === 'mode') device.addComponent(new ShellyComponent(device, 'sys', 'Sys'));
if (key === 'bat') device.addComponent(new ShellyComponent(device, 'battery', 'Battery'));
@@ -830,7 +821,7 @@ export class ShellyDevice extends EventEmitter {
this.log.debug(`*Unknown bthomesensor ${event.component} with event: ${debugStringify(event)}${rs}`);
}
} else if (isValidObject(event) && isValidString(event.event) && isValidString(event.component)) {
- this.log.debug(`Device ${hk}${this.id}${db} has event ${YELLOW}${event.event}${db} from component ${idn}${event.component}${rs}${db}`);
+ this.log.debug(`Device ${hk}${this.id}${db} has event ${YELLOW}${event.event}${db} from component ${idn}${event.component}${rs}${db}${rk}`);
this.getComponent(event.component)?.emit('event', event.component, event.event);
} else {
this.log.debug(`*Unknown event:${rs}\n`, event);
@@ -972,7 +963,13 @@ export class ShellyDevice extends EventEmitter {
if (key === 'tmp') {
if (data.temperature === undefined && data.overtemperature === undefined) this.updateComponent('temperature', data[key] as ShellyData);
const sensor = data.tmp as ShellyData;
- if (sensor.is_valid === true && sensor.value !== undefined) this.getComponent('temperature')?.setValue('value', sensor.value);
+ if (sensor.is_valid === true && sensor.units === 'C' && isValidNumber(sensor.tC, -55, 125)) this.getComponent('temperature')?.setValue('value', sensor.tC);
+ if (sensor.is_valid === true && sensor.units === 'F' && isValidNumber(sensor.tF, -67, 257)) this.getComponent('temperature')?.setValue('value', sensor.tF);
+ }
+ if (key === 'hum') {
+ this.updateComponent('humidity', data[key] as ShellyData);
+ const sensor = data.hum as ShellyData;
+ if (sensor.is_valid === true && isValidNumber(sensor.value, 0, 100)) this.getComponent('humidity')?.setValue('value', sensor.value);
}
if (key === 'temperature') {
if (data[key] !== null && data[key] !== undefined && typeof data[key] === 'number') this.getComponent('sys')?.setValue('temperature', data[key]);
@@ -1207,7 +1204,8 @@ export class ShellyDevice extends EventEmitter {
const controller = new AbortController();
const fetchTimeout = setTimeout(() => {
controller.abort();
- }, 10000);
+ log.debug(`***Aborting fetch device ${host}: service ${service} params ${JSON.stringify(params)}`);
+ }, 20000);
const gen = /^[^A-Z]*$/.test(service) ? 1 : 2;
const url = gen === 1 ? `http://${host}/${service}` : `http://${host}/rpc`;