diff --git a/CHANGELOG.md b/CHANGELOG.md index a5efb09..85bdf8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. ### Added -- [Gen1]: PowerMeter. +- [Gen. 1]: PowerMeter and fix update from gen. 1 devices. Buy me a coffee diff --git a/package.json b/package.json index 7512c70..a0a718c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matterbridge-shelly", - "version": "0.3.2", + "version": "0.3.3", "description": "Matterbridge shelly plugin", "author": "https://github.com/Luligu", "license": "Apache-2.0", diff --git a/src/mock/shellyswitch25-3494546BBF7E.json b/src/mock/shellyswitch25-3494546BBF7E.roller.json similarity index 100% rename from src/mock/shellyswitch25-3494546BBF7E.json rename to src/mock/shellyswitch25-3494546BBF7E.roller.json diff --git a/src/mock/shellyswitch25-3494546BBF7E.switch.json b/src/mock/shellyswitch25-3494546BBF7E.switch.json new file mode 100644 index 0000000..83ba714 --- /dev/null +++ b/src/mock/shellyswitch25-3494546BBF7E.switch.json @@ -0,0 +1,318 @@ +{ + "shelly": { + "type": "SHSW-25", + "mac": "3494546BBF7E", + "auth": false, + "fw": "20230913-112234/v1.14.0-gcb84623", + "discoverable": false, + "longid": 1, + "num_outputs": 2, + "num_meters": 2, + "num_rollers": 1, + "mode": "relay" + }, + "settings": { + "device": { + "type": "SHSW-25", + "mac": "3494546BBF7E", + "hostname": "shellyswitch25-3494546BBF7E", + "num_outputs": 2, + "num_meters": 2, + "num_rollers": 1, + "mode": "relay" + }, + "wifi_ap": { + "enabled": false, + "ssid": "shellyswitch25-3494546BBF7E", + "key": "" + }, + "wifi_sta": { + "enabled": true, + "ssid": "FibreBox_X6-12A4C7", + "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 + }, + "ap_roaming": { + "enabled": true, + "threshold": -70 + }, + "mqtt": { + "enable": false, + "server": "192.168.33.3:1883", + "user": "", + "id": "shellyswitch25-3494546BBF7E", + "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": "192.168.1.189:5683" + }, + "sntp": { + "server": "time.google.com", + "enabled": true + }, + "login": { + "enabled": false, + "unprotected": false, + "username": "admin" + }, + "pin_code": "", + "name": "My Shelly 2.5", + "fw": "20230913-112234/v1.14.0-gcb84623", + "factory_reset_from_switch": true, + "pon_wifi_reset": false, + "discoverable": false, + "build_info": { + "build_id": "20230913-112234/v1.14.0-gcb84623", + "build_timestamp": "2023-09-13T11:22:34Z", + "build_version": "1.0" + }, + "cloud": { + "enabled": true, + "connected": false + }, + "timezone": "Europe/Monaco", + "lat": 43.731201, + "lng": 7.4138, + "tzautodetect": true, + "tz_utc_offset": 7200, + "tz_dst": false, + "tz_dst_auto": true, + "time": "18:29", + "unixtime": 1718987375, + "led_status_disable": false, + "debug_enable": false, + "allow_cross_origin": false, + "actions": { + "active": false, + "names": [ + "btn_on_url", + "btn_off_url", + "longpush_url", + "shortpush_url", + "out_on_url", + "out_off_url", + "btn_on_url", + "btn_off_url", + "longpush_url", + "shortpush_url", + "out_on_url", + "out_off_url", + "roller_open_url", + "roller_close_url", + "roller_stop_url" + ] + }, + "hwinfo": { + "hw_revision": "prod-191217", + "batch_id": 1 + }, + "mode": "relay", + "max_power": 1840, + "longpush_time": 800, + "relays": [ + { + "name": null, + "appliance_type": "General", + "ison": false, + "has_timer": false, + "default_state": "last", + "btn_type": "momentary", + "btn_reverse": 0, + "auto_on": 0, + "auto_off": 0, + "max_power": 0, + "schedule": false, + "schedule_rules": [] + }, + { + "name": null, + "appliance_type": "General", + "ison": false, + "has_timer": false, + "default_state": "last", + "btn_type": "momentary", + "btn_reverse": 0, + "auto_on": 0, + "auto_off": 0, + "max_power": 0, + "schedule": false, + "schedule_rules": [] + } + ], + "rollers": [ + { + "maxtime": 20, + "maxtime_open": 20, + "maxtime_close": 20, + "default_state": "stop", + "swap": false, + "swap_inputs": false, + "input_mode": "openclose", + "button_type": "toggle", + "btn_reverse": 0, + "state": "stop", + "power": 0, + "is_valid": true, + "safety_switch": false, + "schedule": false, + "schedule_rules": [], + "obstacle_mode": "disabled", + "obstacle_action": "stop", + "obstacle_power": 200, + "obstacle_delay": 1, + "ends_delay": 2500, + "safety_mode": "while_opening", + "safety_action": "stop", + "safety_allowed_on_trigger": "none", + "off_power": 2, + "positioning": true + } + ], + "favorites_enabled": false, + "favorites": [ + { + "name": "Position 1", + "pos": 0 + }, + { + "name": "Position 2", + "pos": 0 + }, + { + "name": "Position 3", + "pos": 0 + }, + { + "name": "Position 4", + "pos": 0 + } + ], + "eco_mode_enabled": true + }, + "status": { + "wifi_sta": { + "connected": true, + "ssid": "FibreBox_X6-12A4C7", + "ip": "192.168.1.222", + "rssi": -51 + }, + "cloud": { + "enabled": true, + "connected": false + }, + "mqtt": { + "connected": false + }, + "time": "18:29", + "unixtime": 1718987375, + "serial": 2, + "has_update": false, + "mac": "3494546BBF7E", + "cfg_changed_cnt": 0, + "actions_stats": { + "skipped": 0 + }, + "relays": [ + { + "ison": false, + "has_timer": false, + "timer_started": 0, + "timer_duration": 0, + "timer_remaining": 0, + "overpower": false, + "overtemperature": false, + "is_valid": true, + "source": "input" + }, + { + "ison": false, + "has_timer": false, + "timer_started": 0, + "timer_duration": 0, + "timer_remaining": 0, + "overpower": false, + "overtemperature": false, + "is_valid": true, + "source": "input" + } + ], + "meters": [ + { + "power": 0, + "overpower": 0, + "is_valid": true, + "timestamp": 1718994575, + "counters": [ + 0, + 0, + 0 + ], + "total": 0 + }, + { + "power": 0, + "overpower": 0, + "is_valid": true, + "timestamp": 1718994575, + "counters": [ + 0, + 0, + 0 + ], + "total": 0 + } + ], + "inputs": [ + { + "input": 0, + "event": "", + "event_cnt": 0 + }, + { + "input": 0, + "event": "", + "event_cnt": 0 + } + ], + "temperature": 56.18, + "overtemperature": false, + "tmp": { + "tC": 56.18, + "tF": 133.12, + "is_valid": true + }, + "temperature_status": "Normal", + "update": { + "status": "unknown", + "has_update": false, + "new_version": "", + "old_version": "20230913-112234/v1.14.0-gcb84623" + }, + "ram_total": 50720, + "ram_free": 37864, + "fs_size": 233681, + "fs_free": 146082, + "voltage": 240.93, + "uptime": 26 + } +} \ No newline at end of file diff --git a/src/platform.ts b/src/platform.ts index 75f9713..267aef6 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -127,10 +127,10 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform { if (lightComponent) { let deviceType = DeviceTypes.ON_OFF_LIGHT; if (lightComponent.hasProperty('brightness')) deviceType = DeviceTypes.DIMMABLE_LIGHT; - if (lightComponent.hasProperty('color')) deviceType = DeviceTypes.COLOR_TEMPERATURE_LIGHT; + if (lightComponent.hasProperty('red')) deviceType = DeviceTypes.COLOR_TEMPERATURE_LIGHT; const clusterIds: ClusterId[] = [OnOff.Cluster.id]; if (lightComponent.hasProperty('brightness')) clusterIds.push(LevelControl.Cluster.id); - if (lightComponent.hasProperty('color')) clusterIds.push(ColorControl.Cluster.id); + if (lightComponent.hasProperty('red')) clusterIds.push(ColorControl.Cluster.id); const child = mbDevice.addChildDeviceTypeWithClusterServer(key, [deviceType], clusterIds); mbDevice.addFixedLabel('composed', component.name); // Set the onOff attribute @@ -144,20 +144,20 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform { } // Add command handlers mbDevice.addCommandHandler('on', async (data) => { - this.shellySwitchCommandHandler(mbDevice, data.endpoint.number, device, 'On', true); + this.shellyLightCommandHandler(mbDevice, data.endpoint.number, device, 'On', true); }); mbDevice.addCommandHandler('off', async (data) => { - this.shellySwitchCommandHandler(mbDevice, data.endpoint.number, device, 'Off', false); + this.shellyLightCommandHandler(mbDevice, data.endpoint.number, device, 'Off', false); }); // eslint-disable-next-line @typescript-eslint/no-unused-vars mbDevice.addCommandHandler('moveToLevel', async ({ request, attributes, endpoint }) => { const state = child.getClusterServer(OnOffCluster)?.getOnOffAttribute(); - if (state !== undefined) this.shellySwitchCommandHandler(mbDevice, endpoint.number, device, 'On', state, request.level); + if (state !== undefined) this.shellyLightCommandHandler(mbDevice, endpoint.number, device, 'On', state, request.level); }); // eslint-disable-next-line @typescript-eslint/no-unused-vars mbDevice.addCommandHandler('moveToLevelWithOnOff', async ({ request, attributes, endpoint }) => { const state = child.getClusterServer(OnOffCluster)?.getOnOffAttribute(); - if (state !== undefined) this.shellySwitchCommandHandler(mbDevice, endpoint.number, device, 'On', state, request.level); + if (state !== undefined) this.shellyLightCommandHandler(mbDevice, endpoint.number, device, 'On', state, request.level); }); // Add event handler lightComponent.on('update', (component: string, property: string, value: ShellyDataType) => { @@ -194,10 +194,10 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform { if (state !== undefined) child.getClusterServer(OnOffCluster)?.setOnOffAttribute(state as boolean); // Add command handlers mbDevice.addCommandHandler('on', async (data) => { - this.shellySwitchCommandHandler(mbDevice, data.endpoint.number, device, 'On', true); + this.shellyLightCommandHandler(mbDevice, data.endpoint.number, device, 'On', true); }); mbDevice.addCommandHandler('off', async (data) => { - this.shellySwitchCommandHandler(mbDevice, data.endpoint.number, device, 'Off', false); + this.shellyLightCommandHandler(mbDevice, data.endpoint.number, device, 'Off', false); }); // Add event handler switchComponent.on('update', (component: string, property: string, value: ShellyDataType) => { @@ -450,7 +450,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform { } } - private shellySwitchCommandHandler( + private shellyLightCommandHandler( matterbridgeDevice: MatterbridgeDevice, endpointNumber: EndpointNumber | undefined, shellyDevice: ShellyDevice, @@ -465,7 +465,7 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform { } const endpoint = matterbridgeDevice.getChildEndpoint(endpointNumber); if (!endpoint) { - this.log.error(`shellyCommandHandler error: endpoint undefined for shelly device ${dn}${shellyDevice?.id}${er}`); + this.log.error(`shellyCommandHandler error: endpoint not found for shelly device ${dn}${shellyDevice?.id}${er}`); return false; } // Get the Shelly switch component @@ -651,34 +651,37 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform { `${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}WindowCovering${db} current:${YELLOW}${current}${db} target:${YELLOW}${target}${db} status:${YELLOW}${status?.global}${rs}`, ); } - // Update energy from main components (gen 1 devices send power total inside the component not with meter) - /* - if ( - (shellyComponent.name === 'Light' || - shellyComponent.name === 'Relay' || - shellyComponent.name === 'Switch' || - shellyComponent.name === 'Cover' || - shellyComponent.name === 'Roller') && - property === 'power' - ) { - const cluster = endpoint.getClusterServer(EveHistoryCluster.with(EveHistory.Feature.EveEnergy)); - cluster?.setConsumptionAttribute(value as number); - if (cluster) shellyDevice.log.info(`${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}EveHistory-consumption${db} ${YELLOW}${value as number}${db}`); - } + // Update energy from main components (gen 2 devices send power total inside the component not with meter) if ( - (shellyComponent.name === 'Light' || - shellyComponent.name === 'Relay' || - shellyComponent.name === 'Switch' || - shellyComponent.name === 'Cover' || - shellyComponent.name === 'Roller') && - property === 'total' + shellyComponent.name === 'Light' || + shellyComponent.name === 'Relay' || + shellyComponent.name === 'Switch' || + shellyComponent.name === 'Cover' || + shellyComponent.name === 'Roller' ) { - const cluster = endpoint.getClusterServer(EveHistoryCluster.with(EveHistory.Feature.EveEnergy)); - cluster?.setTotalConsumptionAttribute((value as number) / 1000); // convert to kWh - if (cluster) - shellyDevice.log.info(`${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}EveHistory-totalConsumption${db} ${YELLOW}${(value as number) / 1000}${db}`); + if (property === 'power') { + const cluster = endpoint.getClusterServer(EveHistoryCluster.with(EveHistory.Feature.EveEnergy)); + cluster?.setConsumptionAttribute(value as number); + if (cluster) shellyDevice.log.info(`${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}EveHistory-consumption${db} ${YELLOW}${value as number}${db}`); + } + if (property === 'total') { + const cluster = endpoint.getClusterServer(EveHistoryCluster.with(EveHistory.Feature.EveEnergy)); + cluster?.setTotalConsumptionAttribute((value as number) / 1000); // convert to kWh + if (cluster) + shellyDevice.log.info(`${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}EveHistory-totalConsumption${db} ${YELLOW}${(value as number) / 1000}${db}`); + } + if (property === 'voltage') { + const cluster = endpoint.getClusterServer(EveHistoryCluster.with(EveHistory.Feature.EveEnergy)); + cluster?.setVoltageAttribute(value as number); + if (cluster) shellyDevice.log.info(`${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}EveHistory-voltage${db} ${YELLOW}${value as number}${db}`); + } + if (property === 'current') { + const cluster = endpoint.getClusterServer(EveHistoryCluster.with(EveHistory.Feature.EveEnergy)); + cluster?.setCurrentAttribute(value as number); + if (cluster) shellyDevice.log.info(`${db}Update endpoint ${or}${endpoint.number}${db} attribute ${hk}EveHistory-current${db} ${YELLOW}${value as number}${db}`); + } } - */ + // Update energy from PowerMeter if (shellyComponent.name === 'PowerMeter') { if (property === 'power' || property === 'apower') { diff --git a/src/shellyDevice.ts b/src/shellyDevice.ts index ef2ed10..cd84dce 100644 --- a/src/shellyDevice.ts +++ b/src/shellyDevice.ts @@ -163,6 +163,7 @@ export class ShellyDevice extends EventEmitter { if (key === 'meters') { let index = 0; for (const meter of statusPayload[key] as ShellyData[]) { + if (device.profile === 'cover' && index > 0) break; device.addComponent(new ShellyComponent(device, `meter:${index++}`, 'PowerMeter', meter as ShellyData)); } } @@ -300,6 +301,7 @@ export class ShellyDevice extends EventEmitter { if (key === 'meters') { let index = 0; for (const meter of data[key] as ShellyData[]) { + if (this.profile === 'cover' && index > 0) break; this.updateComponent(`meter:${index++}`, meter as ShellyData); } }