mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-22 05:10:52 +00:00
Compare commits
18 Commits
PROD_2017.
...
revert-zwa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8197097e1d | ||
|
|
36fe6428ab | ||
|
|
2bc44e0205 | ||
|
|
24d079db7c | ||
|
|
283ed425d8 | ||
|
|
1545707ae3 | ||
|
|
2fd8cf1416 | ||
|
|
fe4a5239c7 | ||
|
|
ec781b18be | ||
|
|
43bedd41a8 | ||
|
|
f75e8a6b2d | ||
|
|
515fab9fa4 | ||
|
|
100e696d02 | ||
|
|
3e988ce657 | ||
|
|
00224c2d0b | ||
|
|
f05daf2f34 | ||
|
|
6fae023f7a | ||
|
|
ef8611d2ea |
@@ -11,7 +11,7 @@ metadata {
|
|||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
// capability "Health Check"
|
capability "Health Check"
|
||||||
|
|
||||||
command "getLevel"
|
command "getLevel"
|
||||||
command "getOnOff"
|
command "getOnOff"
|
||||||
@@ -476,7 +476,7 @@ def configure() {
|
|||||||
|
|
||||||
// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time)
|
// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time)
|
||||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||||
// sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
// get ZigBee ID by hidden tile because that's the only way we can do it
|
// get ZigBee ID by hidden tile because that's the only way we can do it
|
||||||
setZigBeeIdTile()
|
setZigBeeIdTile()
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ metadata {
|
|||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Polling"
|
capability "Polling"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
// capability "Health Check"
|
capability "Health Check"
|
||||||
|
|
||||||
command "open"
|
command "open"
|
||||||
command "close"
|
command "close"
|
||||||
|
|
||||||
fingerprint deviceId: "0x1100", inClusters: "0x26,0x72,0x86,0x77,0x80,0x20"
|
fingerprint deviceId: "0x1100", inClusters: "0x26,0x72,0x86,0x77,0x80,0x20"
|
||||||
// fingerprint mfr:"0157", prod:"0100", model:"0100", deviceJoinName: "EcoNet Controls Z-Wave Vent"
|
fingerprint mfr:"0157", prod:"0100", model:"0100", deviceJoinName: "EcoNet Controls Z-Wave Vent"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
@@ -88,7 +88,7 @@ def parse(String description) {
|
|||||||
//send the command to stop polling
|
//send the command to stop polling
|
||||||
def updated() {
|
def updated() {
|
||||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||||
// sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
response("poll stop")
|
response("poll stop")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ metadata {
|
|||||||
definition (name: "Logitech Harmony Hub C2C", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Logitech Harmony Hub C2C", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Media Controller"
|
capability "Media Controller"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
|
capability "Health Check"
|
||||||
|
|
||||||
command "activityoff"
|
command "activityoff"
|
||||||
command "alloff"
|
command "alloff"
|
||||||
@@ -38,6 +39,16 @@ metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def installed() {
|
||||||
|
log.debug "installed()"
|
||||||
|
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "cloud", scheme:"untracked"]), displayed: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
def updated() {
|
||||||
|
log.debug "updated()"
|
||||||
|
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "cloud", scheme:"untracked"]), displayed: false)
|
||||||
|
}
|
||||||
|
|
||||||
def startActivity(String activityId) {
|
def startActivity(String activityId) {
|
||||||
log.debug "Executing 'Start Activity'"
|
log.debug "Executing 'Start Activity'"
|
||||||
log.trace parent.activity("$device.deviceNetworkId-$activityId","start")
|
log.trace parent.activity("$device.deviceNetworkId-$activityId","start")
|
||||||
@@ -58,6 +69,10 @@ def poll() {
|
|||||||
log.trace parent.poll()
|
log.trace parent.poll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ping() {
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "Executing 'Refresh'"
|
log.debug "Executing 'Refresh'"
|
||||||
log.trace parent.poll()
|
log.trace parent.poll()
|
||||||
|
|||||||
@@ -135,10 +135,7 @@ def refresh() {
|
|||||||
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware
|
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware
|
||||||
zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware
|
zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware
|
||||||
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
||||||
zigbee.configureReporting(0xFC45, 0x0000, DataType.INT16, 30, 3600, 100) +
|
|
||||||
zigbee.batteryConfig() +
|
|
||||||
zigbee.temperatureConfig(30, 300)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
@@ -150,5 +147,10 @@ def configure() {
|
|||||||
|
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||||
return refresh()
|
return refresh() +
|
||||||
|
zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0x104E]) + // New firmware
|
||||||
|
zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0xC2DF]) + // Original firmware
|
||||||
|
zigbee.batteryConfig() +
|
||||||
|
zigbee.temperatureConfig(30, 300)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ metadata {
|
|||||||
definition(name: "Wattvision", namespace: "smartthings", author: "Steve Vlaminck") {
|
definition(name: "Wattvision", namespace: "smartthings", author: "Steve Vlaminck") {
|
||||||
capability "Power Meter"
|
capability "Power Meter"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
|
capability "Sensor"
|
||||||
attribute "powerContent", "string"
|
attribute "powerContent", "string"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
devicetypes/smartthings/zigbee-lock.src/README.md
Normal file
46
devicetypes/smartthings/zigbee-lock.src/README.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Zigbee Lock
|
||||||
|
|
||||||
|
Cloud Execution
|
||||||
|
|
||||||
|
Works with:
|
||||||
|
|
||||||
|
* [Yale Push Button Deadbolt (YRD210-HA)](https://www.smartthings.com/works-with-smartthings/door-locks/yale-push-button-deadbolt-yrd210)
|
||||||
|
* [Yale Touchscreen Lever (YRL220-ZB)](https://www.smartthings.com/works-with-smartthings/door-locks/yale-touchscreen-lever-yrl220)
|
||||||
|
* [Yale Touchscreen Deadbolt (YRD220-HA))](https://www.smartthings.com/works-with-smartthings/door-locks/yale-touchscreen-deadbolt-yrd220)
|
||||||
|
* [Yale Key Free Touchscreen Deadbolt (YRD240-HA)](https://www.smartthings.com/works-with-smartthings/door-locks/yale-key-free-touchscreen-deadbolt-yrd240)
|
||||||
|
* [Yale Push Button Lever Lock (YRL210-HA)](https://www.smartthings.com/works-with-smartthings/door-locks/yale-push-button-lever-lock-yrl210)
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
|
||||||
|
* [Capabilities](#capabilities)
|
||||||
|
* [Health](#device-health)
|
||||||
|
* [Battery](#battery-specification)
|
||||||
|
* [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
* **Actuator** - represents that a Device has commands
|
||||||
|
* **Lock** - allows for the control of a lock device
|
||||||
|
* **Refresh** - _refresh()_ command for status updates
|
||||||
|
* **Sensor** - detects sensor events
|
||||||
|
* **Battery** - defines device uses a battery
|
||||||
|
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||||
|
* **Health Check** - indicates ability to get device health notifications
|
||||||
|
|
||||||
|
## Device Health
|
||||||
|
|
||||||
|
Yale Push Button Deadbolt (YRD210-HA) is a Zigbee device and checks in every 1 hour.
|
||||||
|
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*60 + 2)mins = 122 mins.
|
||||||
|
|
||||||
|
* __122min__ checkInterval
|
||||||
|
|
||||||
|
## Battery Specification
|
||||||
|
|
||||||
|
Four AA 1.5V batteries are required.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||||
|
Pairing needs to be tried again by placing the device closer to the hub.
|
||||||
|
Instructions related to pairing, resetting and removing the sensor from SmartThings can be found in the following link:
|
||||||
|
* [Yale Locks Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205138400)
|
||||||
@@ -24,6 +24,7 @@ import physicalgraph.zigbee.zcl.DataType
|
|||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
|
capability "Health Check"
|
||||||
|
|
||||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_5", deviceJoinName: "Kwikset 5-Button Deadbolt"
|
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_5", deviceJoinName: "Kwikset 5-Button Deadbolt"
|
||||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_LEVER_5", deviceJoinName: "Kwikset 5-Button Lever"
|
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_LEVER_5", deviceJoinName: "Kwikset 5-Button Lever"
|
||||||
@@ -83,6 +84,9 @@ def uninstalled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
|
// Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time)
|
||||||
|
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
def cmds =
|
def cmds =
|
||||||
zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE,
|
zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE,
|
||||||
DataType.ENUM8, 0, 3600, null) +
|
DataType.ENUM8, 0, 3600, null) +
|
||||||
@@ -92,6 +96,13 @@ def configure() {
|
|||||||
return refresh() + cmds // send refresh cmds as part of config
|
return refresh() + cmds // send refresh cmds as part of config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
|
* */
|
||||||
|
def ping() {
|
||||||
|
zigbee.readAttribute(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE)
|
||||||
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
def cmds =
|
def cmds =
|
||||||
zigbee.readAttribute(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE) +
|
zigbee.readAttribute(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE) +
|
||||||
|
|||||||
@@ -25,11 +25,13 @@ metadata {
|
|||||||
capability "Switch Level"
|
capability "Switch Level"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
|
capability "Health Check"
|
||||||
capability "Light"
|
capability "Light"
|
||||||
|
|
||||||
command "reset"
|
command "reset"
|
||||||
|
|
||||||
fingerprint inClusters: "0x26,0x32"
|
fingerprint inClusters: "0x26,0x32"
|
||||||
|
fingerprint mfr:"0086", prod:"0003", model:"001B", deviceJoinName: "Aeon Labs Micro Smart Dimmer 2E"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
@@ -99,6 +101,9 @@ def parse(String description) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
|
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||||
|
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
response(refresh())
|
response(refresh())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +168,14 @@ def poll() {
|
|||||||
], 1000)
|
], 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
|
* */
|
||||||
|
def ping() {
|
||||||
|
log.debug "ping() called"
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
delayBetween([
|
delayBetween([
|
||||||
zwave.switchMultilevelV1.switchMultilevelGet().format(),
|
zwave.switchMultilevelV1.switchMultilevelGet().format(),
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
# Z-wave Siren
|
|
||||||
|
|
||||||
Cloud Execution
|
|
||||||
|
|
||||||
Works with:
|
|
||||||
|
|
||||||
* [FortrezZ Siren Strobe Alarm](https://www.smartthings.com/works-with-smartthings/other/fortrezz-water-valve)
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
|
|
||||||
* [Capabilities](#capabilities)
|
|
||||||
* [Health](#device-health)
|
|
||||||
* [Troubleshooting](#troubleshooting)
|
|
||||||
|
|
||||||
## Capabilities
|
|
||||||
|
|
||||||
* **Actuator** - represents that a Device has commands
|
|
||||||
* **Alarm** - allows for interacting with devices that serve as alarms
|
|
||||||
* **Battery** - defines device uses a battery
|
|
||||||
* **Health Check** - indicates ability to get device health notifications
|
|
||||||
* **Polling** - represents that poll() can be implemented for the device
|
|
||||||
* **Refresh** - _refresh()_ command for status updates
|
|
||||||
* **Sensor** - detects sensor events
|
|
||||||
* **Switch** - can detect state (possible values: on/off)
|
|
||||||
|
|
||||||
## Device Health
|
|
||||||
|
|
||||||
FortrezZ Siren Strobe Alarm is polled by the hub.
|
|
||||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
|
||||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
|
||||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
|
||||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
|
||||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
|
||||||
|
|
||||||
* __32min__ checkInterval
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
|
||||||
Pairing needs to be tried again by placing the device closer to the hub.
|
|
||||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
|
||||||
* [FortrezZ Siren Strobe Alarm Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294760-FortrezZ-Siren-Strobe-Alarm)
|
|
||||||
@@ -20,7 +20,6 @@ metadata {
|
|||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Alarm"
|
capability "Alarm"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Health Check"
|
|
||||||
capability "Polling"
|
capability "Polling"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
@@ -28,7 +27,6 @@ metadata {
|
|||||||
|
|
||||||
|
|
||||||
fingerprint inClusters: "0x20,0x25,0x86,0x80,0x85,0x72,0x71"
|
fingerprint inClusters: "0x20,0x25,0x86,0x80,0x85,0x72,0x71"
|
||||||
fingerprint mfr:"0084", prod:"0313", model:"010B", deviceJoinName: "FortrezZ Siren Strobe Alarm"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
@@ -60,11 +58,6 @@ metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated(){
|
|
||||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
|
||||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
|
||||||
}
|
|
||||||
|
|
||||||
def createEvents(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
def createEvents(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||||
def map = [ name: "battery", unit: "%" ]
|
def map = [ name: "battery", unit: "%" ]
|
||||||
if (cmd.batteryLevel == 0xFF) {
|
if (cmd.batteryLevel == 0xFF) {
|
||||||
@@ -126,13 +119,6 @@ def both() {
|
|||||||
on()
|
on()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* PING is used by Device-Watch in attempt to reach the Device
|
|
||||||
* */
|
|
||||||
def ping() {
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "sending battery refresh command"
|
log.debug "sending battery refresh command"
|
||||||
zwave.batteryV1.batteryGet().format()
|
zwave.batteryV1.batteryGet().format()
|
||||||
|
|||||||
@@ -511,6 +511,10 @@ def pollResponse(response, data) {
|
|||||||
if (ResponseValues) {
|
if (ResponseValues) {
|
||||||
def map = [:]
|
def map = [:]
|
||||||
ResponseValues.hubs.each {
|
ResponseValues.hubs.each {
|
||||||
|
// Device-Watch relies on the Logitech Harmony Cloud to get the Device state.
|
||||||
|
def isAlive = it.value.status
|
||||||
|
def d = getChildDevice("harmony-${it.key}")
|
||||||
|
d?.sendEvent(name: "DeviceWatch-DeviceStatus", value: isAlive!=504? "online":"offline", displayed: false, isStateChange: true)
|
||||||
if (it.value.message == "OK") {
|
if (it.value.message == "OK") {
|
||||||
map["${it.key}"] = "${it.value.response.data.currentAvActivity},${it.value.response.data.activityStatus}"
|
map["${it.key}"] = "${it.value.response.data.currentAvActivity},${it.value.response.data.activityStatus}"
|
||||||
def hub = getChildDevice("harmony-${it.key}")
|
def hub = getChildDevice("harmony-${it.key}")
|
||||||
|
|||||||
Reference in New Issue
Block a user