diff --git a/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy b/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy index 908e953ef65..028431906cb 100644 --- a/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy +++ b/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy @@ -1,7 +1,6 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1.3.0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,8 +13,8 @@ preferences { input("LedIntensityParam", "number", title:"Indicator light intensity (1..100) (default: blank)", range:"1..100", description:"optional") input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") } metadata { @@ -71,7 +70,7 @@ def parse(String description) else { traceEvent(settings.logFilter, "send event : $event", settings.trace, get_LOG_DEBUG()) sendEvent(event) - sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } } else @@ -313,35 +312,25 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } } \ No newline at end of file diff --git a/devicetypes/sinope-technologies/rm3250zb-sinope-load-controller.src/rm3250zb-sinope-load-controller.groovy b/devicetypes/sinope-technologies/rm3250zb-sinope-load-controller.src/rm3250zb-sinope-load-controller.groovy index a726d00223f..f0a8cf4c054 100644 --- a/devicetypes/sinope-technologies/rm3250zb-sinope-load-controller.src/rm3250zb-sinope-load-controller.groovy +++ b/devicetypes/sinope-technologies/rm3250zb-sinope-load-controller.src/rm3250zb-sinope-load-controller.groovy @@ -1,7 +1,6 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1.3.0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -11,8 +10,8 @@ metadata { preferences { input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") } definition (name: "RM3250ZB Sinope Load Controller", namespace: "Sinope Technologies", author: "Sinope Technologies", , ocfDeviceType: "oic.d.switch") { @@ -92,18 +91,17 @@ def refresh() { traceEvent(settings.logFilter, "Refresh.", settings.trace, get_LOG_DEBUG()) return zigbee.readAttribute(0x0006, 0x0000) + //read on/off zigbee.readAttribute(0x0B04, 0x050B) + //read active power - zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null) + //configure reporting of on/off - zigbee.configureReporting(0x0B04, 0x050B, 0x29, 30, 599, 0x64) // configure reporting of active power + configure() } def configure() { traceEvent(settings.logFilter, "Configuring Reporting and Bindings.", settings.trace, get_LOG_DEBUG()) //allow 30 minutes without receiving on/off state - sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - return zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null) + // configure reporting of active power - zigbee.configureReporting(0x0B04, 0x050B, 0x29, 30, 599, 0x64) + //configure reporting of on/off + return zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null) + //configure reporting of on/off + zigbee.configureReporting(0x0B04, 0x050B, 0x29, 5, 300, 0x05) + // configure reporting of active power zigbee.readAttribute(0x0006, 0x0000) + //read on/off zigbee.readAttribute(0x0B04, 0x050B) //read active power } @@ -130,35 +128,25 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/devicetypes/sinope-technologies/sw2500zb-sinope-switch.src/sw2500zb-sinope-switch.groovy b/devicetypes/sinope-technologies/sw2500zb-sinope-switch.src/sw2500zb-sinope-switch.groovy index 61a805a02dc..23dca081ba7 100644 --- a/devicetypes/sinope-technologies/sw2500zb-sinope-switch.src/sw2500zb-sinope-switch.groovy +++ b/devicetypes/sinope-technologies/sw2500zb-sinope-switch.src/sw2500zb-sinope-switch.groovy @@ -1,7 +1,6 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1.3.0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -12,8 +11,8 @@ metadata { preferences { input("LedIntensityParam", "number", title:"Indicator light intensity (1..100) (default: blank)", range:"1..100", description:"optional") input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") } definition (name: "SW2500ZB Sinope Switch", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.switch") @@ -58,7 +57,7 @@ def parse(String description) { traceEvent(settings.logFilter, "Event: $event", settings.trace, get_LOG_DEBUG()) sendEvent(event) - sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } else { @@ -123,8 +122,8 @@ def configure() { traceEvent(settings.logFilter, "Configuring Reporting and Bindings", settings.trace, get_LOG_DEBUG()) - //allow 30 min without receiving on/off report - sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + //allow 5 min without receiving on/off report + sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) return zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null) + zigbee.readAttribute(0x0006, 0x0000) @@ -152,36 +151,26 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } } diff --git a/devicetypes/sinope-technologies/th1123zb-th1124zb-sinope-thermostat.src/th1123zb-th1124zb-sinope-thermostat.groovy b/devicetypes/sinope-technologies/th1123zb-th1124zb-sinope-thermostat.src/th1123zb-th1124zb-sinope-thermostat.groovy index dc27a80425d..ed5b31571b2 100644 --- a/devicetypes/sinope-technologies/th1123zb-th1124zb-sinope-thermostat.src/th1123zb-th1124zb-sinope-thermostat.groovy +++ b/devicetypes/sinope-technologies/th1123zb-th1124zb-sinope-thermostat.src/th1123zb-th1124zb-sinope-thermostat.groovy @@ -1,6 +1,6 @@ /** Copyright Sinopé Technologies -1.2.0 +1.3.0 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. **/ @@ -642,35 +642,25 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.substring(0,1).toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - // def results = [ - // name: "verboseTrace", - // value: message, - // displayed: ((displayEvent) ?: false) - // ] - - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - // if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } } \ No newline at end of file diff --git a/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy b/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy index 264de97ac04..17e229d8187 100644 --- a/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy +++ b/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy @@ -1,50 +1,52 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1.3.0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. **/ metadata { - -preferences { - input("AirFloorModeParam", "enum", title: "Control mode (Default: Floor)", description:"Control mode using the floor or ambient temperature.", options: ["Ambient", "Floor"], multiple: false, required: false) - input("BacklightAutoDimParam", "enum", title:"Backlight setting (Default: Sensing)", description: "On Demand or Sensing", options: ["Sensing", "On Demand"], multiple: false, required: false) - input("KbdLockParam", "enum", title: "Keypad lock (Default: Unlocked)", description: "Enable or disable the device's buttons.",options: ["Lock", "Unlock"], multiple: false, required: false) - input("TimeFormatParam", "enum", title:"Time Format (Default: 24h)", description: "Time format displayed by the device.", options:["24h", "12h AM/PM"], multiple: false, required: false) - input("DisableOutdorTemperatureParam", "bool", title: "enable/disable outdoor temperature", description: "Set it to true to Disable outdoor temperature on the thermostat") - input("FloorSensorTypeParam", "enum", title:"Probe type (Default: 10k)", description: "Choose probe type.", options: ["10k", "12k"], multiple: false, required: false) - input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit (5C to 36C / 41F to 96)", range:("5..96"), - description: "The maximum ambient temperature limit when in floor control mode.", required: false) - input("FloorLimitMinParam", "number", title:"Floor low limit (5C to 36C / 41F to 96F)", range:("5..96"), description: "The minimum temperature limit of the floor when in ambient control mode.", required: false) - input("FloorLimitMaxParam", "number", title:"Floor high limit (5C to 36C / 41F to 96F)", range:("5..96"), description: "The maximum temperature limit of the floor when in ambient control mode.", required: false) - input("AuxLoadParam", "number", title:"Auxiliary load value (Default: 0)", range:("0..65535"), - description: "Enter the power in watts of the heating element connected to the auxiliary output.", required: false) - input("trace", "bool", title: "Trace (Only for debugging)", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") -} - - + preferences { + input("AirFloorModeParam", "enum", title: "Control mode (Default: Floor)", + description:"Control mode using the floor or ambient temperature.", options: ["Ambient", "Floor"], multiple: false, required: false) + input("BacklightAutoDimParam", "enum", title:"Backlight setting (Default: Always ON)", + description: "On Demand or Always ON", options: ["On Demand", "Always ON"], multiple: false, required: false) + input("KbdLockParam", "enum", title: "Keypad lock (Default: Unlocked)", + description: "Enable or disable the device's buttons.",options: ["Lock", "Unlock"], multiple: false, required: false) + input("TimeFormatParam", "enum", title:"Time Format (Default: 24h)", + description: "Time format displayed by the device.", options:["12h AM/PM", "24h"], multiple: false, required: false) + input("DisableOutdorTemperatureParam", "enum", title: "Secondary display (Default: Outside temp.)", multiple: false, required: false, options: ["Setpoint", "Outside temp."], + description: "Information displayed in the secondary zone of the device") + input("FloorSensorTypeParam", "enum", title:"Probe type (Default: 10k)", + description: "Choose floor sensors probe. The floor sensor provided with the thermostats are 10K.", options: ["10k", "12k"], multiple: false, required: false) + + input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit (5C to 36C / 41F to 96)", range: "5..96", + description: "The maximum ambient temperature limit when in floor control mode.", required: false) + input("FloorLimitMinParam", "number", title:"Floor low limit (5C to 34C / 41F to 93F)", range: "5..93", + description: "The minimum temperature limit of the floor when in ambient control mode.", required: false) + input("FloorLimitMaxParam", "number", title:"Floor high limit (7C to 36C / 45F to 96F)", range: "7..96", + description: "The maximum temperature limit of the floor when in ambient control mode.", required: false) + // input("AuxLoadParam", "number", title:"Auxiliary load value (Default: 0)", range:("0..65535"), + // description: "Enter the power in watts of the heating element connected to the auxiliary output.", required: false) + input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + } definition(name: "TH1300ZB Sinope Thermostat", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.thermostat") { - capability "thermostatHeatingSetpoint" - capability "thermostatMode" - capability "thermostatOperatingState" - capability "thermostatSetpoint" - capability "Actuator" capability "Temperature Measurement" + capability "Thermostat" + capability "Thermostat Heating Setpoint" + capability "Thermostat Mode" + capability "Thermostat Operating State" + capability "Actuator" capability "Configuration" + capability "Health check" capability "Refresh" capability "Sensor" - capability "lock" - attribute "temperatureDisplayMode", "enum", ["Deg_C", "Deg_F"] - attribute "occupancyStatus", "enum", ["unoccupy", "occupy"] attribute "outdoorTemp", "string" attribute "heatingSetpointRange", "VECTOR3" - attribute "verboseTrace", "string" attribute "gfciStatus", "enum", ["OK", "error"] attribute "floorLimitStatus", "enum", ["OK", "floorLimitLowReached", "floorLimitMaxReached", "floorAirLimitLowReached", "floorAirLimitMaxReached"] @@ -53,6 +55,7 @@ preferences { fingerprint manufacturer: "Sinope Technologies", model: "TH1300ZB", deviceJoinName: "Sinope Thermostat" //Sinope TH1300ZB Thermostat } + simulator { } //-------------------------------------------------------------------------------------------------------- tiles(scale: 2) { @@ -65,16 +68,12 @@ preferences { attributeState("VALUE_DOWN", action: "heatLevelDown") } tileAttribute("device.heatingDemand", key: "SECONDARY_CONTROL") { - attributeState("default", label: '${currentValue}%', unit: "%") + attributeState("default", label: '${currentValue}%', unit: "%", icon:"st.Weather.weather2") } tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") { attributeState("idle", backgroundColor: "#44b621") attributeState("heating", backgroundColor: "#ffa81e") } - tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") { - attributeState("off", label: '${name}') - attributeState("heat", label: '${name}') - } tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") { attributeState("default", label: '${currentValue}', unit: "dF") } @@ -110,46 +109,28 @@ preferences { //-- Main & Details ---------------------------------------------------------------------------------------- main("thermostatMulti") - details(["thermostatMulti", - "heatingSetpointSlider", - "thermostatMode", - "gfciStatus", - "floorLimitStatus", - "refresh" - ]) + details(["thermostatMulti", "heatingSetpointSlider", "thermostatMode", "gfciStatus", "floorLimitStatus", "refresh"]) } } -def getBackgroundColors() { - def results - if (state?.scale == 'C') { - // Celsius Color Range - results = [ - [value: 0, color: "#153591"], - [value: 7, color: "#1e9cbb"], - [value: 15, color: "#90d2a7"], - [value: 23, color: "#44b621"], - [value: 29, color: "#f1d801"], - [value: 35, color: "#d04e00"], - [value: 37, color: "#bc2323"] - ] - } else { - results = - // Fahrenheit Color Range - [ - [value: 31, color: "#153591"], - [value: 44, color: "#1e9cbb"], - [value: 59, color: "#90d2a7"], - [value: 74, color: "#44b621"], - [value: 84, color: "#f1d801"], - [value: 95, color: "#d04e00"], - [value: 96, color: "#bc2323"] - ] - } - return results - +def getThermostatSetpointRange() { + (getTemperatureScale() == "C") ? [5, 36] : [41, 96] +} + +def getHeatingSetpointRange() { + thermostatSetpointRange +} + +def getSupportedThermostatModes() { + ["heat", "off"] } +def configureSupportedRanges() { + sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: false) + // These are part of the deprecated Thermostat capability. Remove these when that capability is removed. + sendEvent(name: "thermostatSetpointRange", value: thermostatSetpointRange, displayed: false) + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, displayed: false) +} //-- Installation ---------------------------------------------------------------------------------------- @@ -161,8 +142,7 @@ def installed() { } def updated() { - - if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 15000) { + if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 1000) { state.updatedLastRanAt = now() def cmds = [] @@ -175,25 +155,16 @@ def updated() { runEvery15Minutes(refresh_misc) - if(AirFloorModeParam == "Ambient"){//Air mode - traceEvent(settings.logFilter,"Set to Ambient mode",settings.trace) - cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0001) - } - else{//Floor mode + if(AirFloorModeParam == "Floor" || AirFloorModeParam == '1'){//Floor mode traceEvent(settings.logFilter,"Set to Floor mode",settings.trace) cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0002) } - - if(KbdLockParam == "Lock"){ - traceEvent(settings.logFilter,"device lock",settings.trace) - lock() - } - else{ - traceEvent(settings.logFilter,"device unlock",settings.trace) - unlock() + else{//Air mode + traceEvent(settings.logFilter,"Set to Floor mode",settings.trace) + cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0001) } - if(TimeFormatParam == "12h AM/PM"){//12h AM/PM + if(TimeFormatParam == "12h AM/PM" || TimeFormatParam == '0'){//12h AM/PM traceEvent(settings.logFilter,"Set to 12h AM/PM",settings.trace) cmds += zigbee.writeAttribute(0xFF01, 0x0114, 0x30, 0x0001) } @@ -202,7 +173,7 @@ def updated() { cmds += zigbee.writeAttribute(0xFF01, 0x0114, 0x30, 0x0000) } - if(BacklightAutoDimParam == "On Demand"){ //Backlight when needed + if(BacklightAutoDimParam == "On Demand" || BacklightAutoDimParam == '0'){ //Backlight when needed traceEvent(settings.logFilter,"Backlight on press",settings.trace) cmds += zigbee.writeAttribute(0x0201, 0x0402, 0x30, 0x0000) } @@ -211,7 +182,7 @@ def updated() { cmds += zigbee.writeAttribute(0x0201, 0x0402, 0x30, 0x0001) } - if(FloorSensorTypeParam == "12k"){//sensor type = 12k + if(FloorSensorTypeParam == "12k" || FloorSensorTypeParam == '1'){//sensor type = 12k traceEvent(settings.logFilter,"Sensor type is 12k",settings.trace) cmds += zigbee.writeAttribute(0xFF01, 0x010B, 0x30, 0x0001) } @@ -299,41 +270,29 @@ def updated() { void initialize() { state?.scale = getTemperatureScale() - def supportedThermostatModes = ['off', 'heat'] state?.supportedThermostatModes = supportedThermostatModes - sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: (settings.trace ?: false)) + + configureSupportedRanges(); updated()//some thermostats values are not restored to a default value when disconnected. //executing the updated function make sure the thermostat parameters and the app parameters are in sync - if(state?.scale == 'C') - { - sendEvent(name: "heatingSetpointRange", value: [5.0, 36.0], scale: scale) - } - else if(state?.scale == 'F') - { - sendEvent(name: "heatingSetpointRangeLow", value: [41,96], scale: scale) - } - - sendEvent(name: "lock", value: "unlocked") - //for some reasons, the "runIn()" is not working in the "initialize()" of this driver. //to go around the problem, a read and a configuration is sent to each attribute required dor a good behaviour of the application def cmds = [] cmds += zigbee.readAttribute(0x0204, 0x0000) // Rd thermostat display mode if (state?.scale == 'C') { cmds += zigbee.writeAttribute(0x0204, 0x0000, 0x30, 0) // Wr °C on thermostat display - sendEvent(name: "heatingSetpointRange", value: [5,36], scale: state.scale) + sendEvent(name: "heatingSetpointRange", value: [5,36], scale: state?.scale) } else { cmds += zigbee.writeAttribute(0x0204, 0x0000, 0x30, 1) // Wr °F on thermostat display - sendEvent(name: "heatingSetpointRange", value: [41,96], scale: state.scale) + sendEvent(name: "heatingSetpointRange", value: [41,96], scale: state?.scale) } cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature cmds += zigbee.readAttribute(0x0201, 0x0012) // Rd thermostat Occupied heating setpoint cmds += zigbee.readAttribute(0x0201, 0x0008) // Rd thermostat PI heating demand cmds += zigbee.readAttribute(0x0201, 0x001C) // Rd thermostat System Mode - cmds += zigbee.readAttribute(0x0204, 0x0001) // Rd thermostat Keypad lockout cmds += zigbee.readAttribute(0xFF01, 0x0105) // Rd thermostat Control mode cmds += zigbee.readAttribute(0xFF01, 0x0115) // Rd GFCI status @@ -350,12 +309,14 @@ void initialize() { def configure() { traceEvent(settings.logFilter, "Configuring Reporting and Bindings", settings.trace, get_LOG_DEBUG()) - //allow 30 min without receiving on/off report - return sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + //allow 5 min without receiving temperature report + return sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } def ping() { - refresh() + def cmds = []; + cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature + sendZigbeeCommands(cmds) } def uninstalled() { @@ -393,10 +354,12 @@ def parse(String description) { def createCustomMap(descMap){ def result = null def map = [: ] - def scale = getTemperatureScale() + def scale = temperatureScale if (descMap.cluster == "0201" && descMap.attrId == "0000") { + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) map.name = "temperature" - map.value = getTemperatureValue(descMap.value, true) + map.value = getTemperatureValue(descMap.value, false) + map.unit = scale if(map.value > 158) {//if the value of the temperature is over 128C, it is considered an error with the temperature sensor map.value = "Sensor Error" @@ -414,13 +377,12 @@ def createCustomMap(descMap){ } } traceEvent(settings.logFilter, "parse>ACTUAL TEMP: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value, unit: scale) - sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + //allow 5 min without receiving temperature report + sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } else if (descMap.cluster == "0201" && descMap.attrId == "0008") { map.name = "heatingDemand" map.value = getHeatingDemand(descMap.value) - sendEvent(name: map.name, value: map.value) traceEvent(settings.logFilter, "parse>${map.name}: ${map.value}") def operatingState = (map.value.toInteger() < 10) ? "idle" : "heating" sendEvent(name: "thermostatOperatingState", value: operatingState) @@ -428,23 +390,18 @@ def createCustomMap(descMap){ } else if (descMap.cluster == "0201" && descMap.attrId == "0012") { + configureSupportedRanges(); + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) map.name = "heatingSetpoint" map.value = getTemperatureValue(descMap.value, true) - sendEvent(name: map.name, value: map.value, unit: scale) + map.unit = scale traceEvent(settings.logFilter, "parse>OCCUPY: ${map.name}: ${map.value}, scale: ${scale} ", settings.trace) } else if (descMap.cluster == "0201" && descMap.attrId == "001c") { map.name = "thermostatMode" map.value = getModeMap()[descMap.value] - sendEvent(name: map.name, value: map.value) traceEvent(settings.logFilter, "parse>${map.name}: ${map.value}", settings.trace) } - else if (descMap.cluster == "0204" && descMap.attrId == "0001") { - map.name = "keypadLockStatus" - map.value = getLockMap()[descMap.value] - traceEvent(settings.logFilter, "parse>KEYPAD LOCK STATUS: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value) - } else if (descMap.cluster == "FF01" && descMap.attrId == "010c") { map.name = "floorLimitStatus" if(descMap.value.toInteger() == 0){ @@ -458,19 +415,24 @@ def createCustomMap(descMap){ }else{ map.value = "floorAirLimitMaxReached" } + if(map.value != "OK"){ + log.warn map.value + } traceEvent(settings.logFilter, "parse>floorLimitStatus: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value) } else if (descMap.cluster == "FF01" && descMap.attrId == "0115") { map.name = "gfciStatus" if(descMap.value.toInteger() == 0){ map.value = "OK" }else if(descMap.value.toInteger() == 1){ + log.error("Ground Fault Circuit Interrupter (GFCI)") map.value = "error" } traceEvent(settings.logFilter, "parse>gfciStatus: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value) } + if(map){ + result = createEvent(map); + } return result } @@ -563,8 +525,6 @@ def setHeatingSetpoint(degrees) { } else { tempValueString = String.format('%2d', degreesDouble.intValue()) } - sendEvent("name": "heatingSetpoint", "value": tempValueString, scale: scale) - sendEvent("name": "thermostatSetpoint", "value": tempValueString, scale: scale) traceEvent(settings.logFilter, "setHeatingSetpoint> new setPoint: $tempValueString", settings.trace) def celsius = (scale == "C") ? degreesDouble : (fahrenheitToCelsius(degreesDouble) as Double).round(1) def cmds = [] @@ -601,19 +561,9 @@ def getModeMap() { ] } -def getSupportedThermostatModes() { - - if (!state?.supportedThermostatModes) { - state?.supportedThermostatModes = (device.currentValue("supportedThermostatModes")) ? - device.currentValue("supportedThermostatModes").toString().minus('[').minus(']').tokenize(',') : ['off', 'heat'] - } - return state?.supportedThermostatModes -} - def setThermostatMode(mode) { traceEvent(settings.logFilter, "setThermostatMode>switching thermostatMode", settings.trace) mode = mode?.toLowerCase() - def supportedThermostatModes = getSupportedThermostatModes() if (mode in supportedThermostatModes) { "mode_$mode" () @@ -624,79 +574,30 @@ def setThermostatMode(mode) { def mode_off() { traceEvent(settings.logFilter, "off>begin", settings.trace) - sendEvent(name: "thermostatMode", value: "off", data: [supportedThermostatModes: getSupportedThermostatModes()]) def cmds = [] cmds += zigbee.writeAttribute(0x0201, 0x001C, 0x30, 0) - cmds += zigbee.readAttribute(0x0201, 0x0008) - sendZigbeeCommands(cmds) + cmds += zigbee.readAttribute(0x0201, 0x001C) traceEvent(settings.logFilter, "off>end", settings.trace) + sendZigbeeCommands(cmds) } def mode_heat() { traceEvent(settings.logFilter, "heat>begin", settings.trace) - sendEvent(name: "thermostatMode", value: "heat", data: [supportedThermostatModes: getSupportedThermostatModes()]) def cmds = [] cmds += zigbee.writeAttribute(0x0201, 0x001C, 0x30, 4) - cmds += zigbee.readAttribute(0x0201, 0x0008) - sendZigbeeCommands(cmds) + cmds += zigbee.readAttribute(0x0201, 0x001C) traceEvent(settings.logFilter, "heat>end", settings.trace) -} -//-- Keypad Lock ----------------------------------------------------------------------------------------- - -def keypadLockLevel() { - ["unlock", "lock"] //only those level are used for the moment -} - -def getLockMap() { - [ - "00": "unlocked", - "01": "locked", - ] -} - -def unlock() { - traceEvent(settings.logFilter, "unlock>begin", settings.trace) - sendEvent(name: "lock", value: "unlocked") - def cmds = [] - cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x00) sendZigbeeCommands(cmds) - traceEvent(settings.logFilter, "unlock>end", settings.trace) -} - -def lock() { - traceEvent(settings.logFilter, "lock>begin", settings.trace) - sendEvent(name: "lock", value: "locked") - def cmds = [] - cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x01) - sendZigbeeCommands(cmds) - traceEvent(settings.logFilter, "lock>end", settings.trace) } def refresh() { - if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 20000) { // Check if last update > 20 sec + if (true || !state.updatedLastRanAt || now() >= state.updatedLastRanAt + 5000) { // Check if last update > 5 sec state.updatedLastRanAt = now() state?.scale = getTemperatureScale() traceEvent(settings.logFilter, "refresh>scale=${state.scale}", settings.trace) def cmds = [] - def heatingSetpointRangeHigh - def heatingSetpointRangeLow - if(state?.scale == 'C') - { - heatingSetpointRangeLow = 5.0 - heatingSetpointRangeHigh = 36.0 - } - else if(state?.scale == 'F') - { - heatingSetpointRangeLow = 41 - heatingSetpointRangeHigh = 96 - } - def low = heatingSetpointRangeLow.toFloat().round(1) - def high = heatingSetpointRangeHigh.toFloat().round(1) - def heatingSetpointRange= [low,high] - sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) - cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature cmds += zigbee.readAttribute(0x0201, 0x0012) // Rd thermostat Occupied heating setpoint cmds += zigbee.readAttribute(0x0201, 0x0008) // Rd thermostat PI heating demand @@ -707,12 +608,6 @@ def refresh() { cmds += zigbee.readAttribute(0xFF01, 0x0105) // Rd thermostat Control mode cmds += zigbee.readAttribute(0xFF01, 0x0115) // Rd GFCI status - cmds += zigbee.configureReporting(0x0201, 0x0000, 0x29, 19, 300, 25) // local temperature - cmds += zigbee.configureReporting(0x0201, 0x0008, 0x0020, 11, 301, 10) // heating demand - cmds += zigbee.configureReporting(0x0201, 0x0012, 0x0029, 8, 302, 40) // occupied heating setpoint - cmds += zigbee.configureReporting(0xFF01, 0x0115, 0x30, 10, 3600, 1) // report gfci status each hours - cmds += zigbee.configureReporting(0xFF01, 0x010C, 0x30, 10, 3600, 1) // floor limit status each hours - sendZigbeeCommands(cmds) refresh_misc() } @@ -735,8 +630,10 @@ void refresh_misc() { int outdoorTempValue int outdoorTempToSend - if(!settings.DisableOutdorTemperatureParam) - { + if(DisableOutdorTemperatureParam == "Setpoint" || DisableOutdorTemperatureParam == "0"){//delete outdoorTemp + cmds += zigbee.writeAttribute(0xFF01, 0x0010, 0x29, 0x8000) + } + else{ cmds += zigbee.writeAttribute(0xFF01, 0x0011, 0x21, 10800)//set the outdoor temperature timeout to 3 hours if (outdoorTemp < 0) { outdoorTempValue = -outdoorTemp*100 - 65536 @@ -751,12 +648,6 @@ void refresh_misc() { cmds += zigbee.writeAttribute(0xFF01, 0x0010, 0x29, outdoorTempToSend, [mfgCode: 0x119C]) } } - else - {//delete outdoorTemp - //the outdoor temperature cannot be directly erased from the thermostat. - //to erase it rapidly, the external temperature timeout must be set to the minimal value (30sec) - cmds += zigbee.writeAttribute(0xFF01, 0x0011, 0x21, 30)//set the outdoor temperature timeout to 30sec - } def mytimezone = location.getTimeZone() long dstSavings = 0 @@ -786,7 +677,7 @@ void refresh_misc() { //-- Private functions ----------------------------------------------------------------------------------- -void sendZigbeeCommands(cmds, delay = 1000) { +void sendZigbeeCommands(cmds, delay = 250) { cmds.removeAll { it.startsWith("delay") } // convert each command into a HubAction cmds = cmds.collect { new physicalgraph.device.HubAction(it) } @@ -879,35 +770,25 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy b/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy index 96372d45511..f3d244beea1 100644 --- a/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy +++ b/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy @@ -1,51 +1,57 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1.3.0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. **/ metadata { - -preferences { - input("AirFloorModeParam", "enum", title: "Control mode (Default: Floor)", description:"Control mode using the floor or ambient temperature.", options: ["Ambient", "Floor"], multiple: false, required: false) - input("BacklightAutoDimParam", "enum", title:"Backlight setting (Default: Sensing)", description: "On Demand or Sensing", options: ["Sensing", "On Demand"], multiple: false, required: false) - input("KbdLockParam", "enum", title: "Keypad lock (Default: Unlocked)", description: "Enable or disable the device's buttons.",options: ["Lock", "Unlock"], multiple: false, required: false) - input("TimeFormatParam", "enum", title:"Time Format (Default: 24h)", description: "Time format displayed by the device.", options:["24h", "12h AM/PM"], multiple: false, required: false) - input("DisableOutdorTemperatureParam", "bool", title: "enable/disable outdoor temperature", description: "Set it to true to Disable outdoor temperature on the thermostat") - input("FloorSensorTypeParam", "enum", title:"Probe type (Default: 10k)", description: "Choose probe type.", options: ["10k", "12k"], multiple: false, required: false) - input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit (5C to 36C / 41F to 96", range:("5..96"), - description: "The maximum ambient temperature limit when in floor control mode.", required: false) - input("FloorLimitMinParam", "number", title:"Floor low limit (5C to 36C / 41F to 96F)", range:("5..96"), description: "The minimum temperature limit of the floor when in ambient control mode.", required: false) - input("FloorLimitMaxParam", "number", title:"Floor high limit (5C to 36C / 41F to 96F)", range:("5..96"), description: "The maximum temperature limit of the floor when in ambient control mode.", required: false) - input("AuxLoadParam", "number", title:"Auxiliary load value (Default: 0)", range:("0..65535"), - description: "Enter the power in watts of the heating element connected to the auxiliary output.", required: false) - input("AuxiliaryCycleLengthParam", "enum", title:"Auxiliary cycle ltngth", options: ["disable, 15 seconds", "30 minutes"], required: false) - input("trace", "bool", title: "Trace (Only for debugging)", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") -} - - + preferences { + input("AirFloorModeParam", "enum", title: "Control mode (Default: Ambient)", + description:"Control mode using the floor or ambient temperature.", options: ["Ambient", "Floor"], multiple: false, required: false) + input("BacklightAutoDimParam", "enum", title:"Backlight setting (Default: Always ON)", + description: "On Demand or Always ON", options: ["On Demand", "Always ON"], multiple: false, required: false) + input("KbdLockParam", "enum", title: "Keypad lock (Default: Unlocked)", + description: "Enable or disable the device's buttons.",options: ["Lock", "Unlock"], multiple: false, required: false) + input("TimeFormatParam", "enum", title:"Time Format (Default: 24h)", + description: "Time \nformat \ndisplayed \nby the device.", options:["12h AM/PM", "24h"], multiple: false, required: false) + input("DisableOutdorTemperatureParam", "enum", title: "Secondary display (Default: Outside temp.)", multiple: false, required: false, options: ["Setpoint", "Outside temp."], + description: "Information displayed in the \nsecondary zone of the device") + input("FloorSensorTypeParam", "enum", title:"Probe type (Default: 10k)", + description: "Choose floor sensors probe. The floor sensor provided with the thermostats are 10K.", options: ["10k", "12k"], multiple: false, required: false) + + input("AuxiliaryCycleLengthParam", "enum", title:"Auxiliary cycle length", options: ["disable, 15 seconds", "30 minutes"], required: false) + + // input("PumpProtectionParam", "enum", titile: "Pump Protection (Default: Off)", options: ["On", "Off"], required: false + // description: "Activate the main output 1 minute every 24 hours to ensure the hydronics system pump does not seize.") + + input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit (5C to 36C / 41F to 96)", range: "5..96", + description: "The maximum ambient temperature limit \nwhen in floor control mode.", required: false) + input("FloorLimitMinParam", "number", title:"Floor low limit (5C to 34C / 41F to 93F)", range: "5..93", + description: "The minimum temperature limit of the floor when in ambient control mode.", required: false) + input("FloorLimitMaxParam", "number", title:"Floor high limit (7C to 36C / 45F to 96F)", range: "7..96", + description: "The maximum temperature limit of the floor when in ambient control mode.", required: false) + // input("AuxLoadParam", "number", title:"Auxiliary load value (Default: 0)", range:("0..65535"), + // description: "Enter the power in watts of the heating element connected to the auxiliary output.", required: false) + input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + } definition(name: "TH1400ZB Sinope Thermostat", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.thermostat") { - capability "thermostatHeatingSetpoint" - capability "thermostatMode" - capability "thermostatOperatingState" - capability "thermostatSetpoint" - capability "Actuator" capability "Temperature Measurement" + capability "Thermostat" + capability "Thermostat Heating Setpoint" + capability "Thermostat Mode" + capability "Thermostat Operating State" + capability "Actuator" capability "Configuration" + capability "Health check" capability "Refresh" capability "Sensor" - capability "lock" - attribute "temperatureDisplayMode", "enum", ["Deg_C", "Deg_F"] - attribute "occupancyStatus", "enum", ["unoccupy", "occupy"] attribute "outdoorTemp", "string" attribute "heatingSetpointRange", "VECTOR3" - attribute "verboseTrace", "string" attribute "floorLimitStatus", "enum", ["OK", "floorLimitLowReached", "floorLimitMaxReached", "floorAirLimitLowReached", "floorAirLimitMaxReached"] command "heatLevelUp" @@ -53,6 +59,7 @@ preferences { fingerprint manufacturer: "Sinope Technologies", model: "TH1400ZB", deviceJoinName: "Sinope Thermostat", mnmn: "SmartThings", vid: "SmartThings-smartthings-TH1300ZB_Sinope_Thermostat" //Sinope TH1400ZB Thermostat } + simulator { } //-------------------------------------------------------------------------------------------------------- tiles(scale: 2) { @@ -65,16 +72,12 @@ preferences { attributeState("VALUE_DOWN", action: "heatLevelDown") } tileAttribute("device.heatingDemand", key: "SECONDARY_CONTROL") { - attributeState("default", label: '${currentValue}%', unit: "%") + attributeState("default", label: '${currentValue}%', unit: "%", icon:"st.Weather.weather2") } tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") { attributeState("idle", backgroundColor: "#44b621") attributeState("heating", backgroundColor: "#ffa81e") } - tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") { - attributeState("off", label: '${name}') - attributeState("heat", label: '${name}') - } tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") { attributeState("default", label: '${currentValue}', unit: "dF") } @@ -106,12 +109,7 @@ preferences { //-- Main & Details ---------------------------------------------------------------------------------------- main("thermostatMulti") - details(["thermostatMulti", - "heatingSetpointSlider", - "thermostatMode", - "floorLimitStatus", - "refresh" - ]) + details(["thermostatMulti", "heatingSetpointSlider", "thermostatMode", "floorLimitStatus", "refresh"]) } } @@ -145,6 +143,24 @@ def getBackgroundColors() { } +def getThermostatSetpointRange() { + (getTemperatureScale() == "C") ? [5, 36] : [41, 96] +} + +def getHeatingSetpointRange() { + thermostatSetpointRange +} + +def getSupportedThermostatModes() { + ["heat", "off"] +} + +def configureSupportedRanges() { + sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: false) + // These are part of the deprecated Thermostat capability. Remove these when that capability is removed. + sendEvent(name: "thermostatSetpointRange", value: thermostatSetpointRange, displayed: false) + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, displayed: false) +} //-- Installation ---------------------------------------------------------------------------------------- @@ -156,8 +172,7 @@ def installed() { } def updated() { - - if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 15000) { + if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 1000) { state.updatedLastRanAt = now() def cmds = [] @@ -168,27 +183,28 @@ def updated() { traceEvent(settings.logFilter, "updated>exception $e, continue processing", settings.trace, get_LOG_ERROR()) } + runIn(1,refresh_misc) runEvery15Minutes(refresh_misc) - if(AirFloorModeParam == "Ambient"){//Air mode + if(AirFloorModeParam == "Floor" || AirFloorModeParam == '1'){//Air mode traceEvent(settings.logFilter,"Set to Ambient mode",settings.trace) - cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0001) + cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0002) } else{//Floor mode traceEvent(settings.logFilter,"Set to Floor mode",settings.trace) - cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0002) + cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0001) } - if(KbdLockParam == "Lock"){ + if(KbdLockParam == "Lock" || KbdLockParam == '0'){ traceEvent(settings.logFilter,"device lock",settings.trace) - lock() + cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x01) } else{ traceEvent(settings.logFilter,"device unlock",settings.trace) - unlock() + cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x00) } - if(TimeFormatParam == "12h AM/PM"){//12h AM/PM + if(TimeFormatParam == "12h AM/PM" || TimeFormatParam == '0'){//12h AM/PM traceEvent(settings.logFilter,"Set to 12h AM/PM",settings.trace) cmds += zigbee.writeAttribute(0xFF01, 0x0114, 0x30, 0x0001) } @@ -197,7 +213,7 @@ def updated() { cmds += zigbee.writeAttribute(0xFF01, 0x0114, 0x30, 0x0000) } - if(BacklightAutoDimParam == "On Demand"){ //Backlight when needed + if(BacklightAutoDimParam == "On Demand" || BacklightAutoDimParam == '0'){ //Backlight when needed traceEvent(settings.logFilter,"Backlight on press",settings.trace) cmds += zigbee.writeAttribute(0x0201, 0x0402, 0x30, 0x0000) } @@ -206,7 +222,7 @@ def updated() { cmds += zigbee.writeAttribute(0x0201, 0x0402, 0x30, 0x0001) } - if(FloorSensorTypeParam == "12k"){//sensor type = 12k + if(FloorSensorTypeParam == "12k" || FloorSensorTypeParam == '1'){//sensor type = 12k traceEvent(settings.logFilter,"Sensor type is 12k",settings.trace) cmds += zigbee.writeAttribute(0xFF01, 0x010B, 0x30, 0x0001) } @@ -215,6 +231,16 @@ def updated() { cmds += zigbee.writeAttribute(0xFF01, 0x010B, 0x30, 0x0000) } + // if(PumpProtectionParam == "On" || FloorSensorTypeParam == '0'){//sensor type = 12k + // traceEvent(settings.logFilter,"Sensor type is 12k",settings.trace) + // cmds += zigbee.writeAttribute(0xFF01, 0x010B, 0x30, 0x0001) + // } + // else{//sensor type = 10k + // traceEvent(settings.logFilter,"Sensor type is 10k",settings.trace) + // cmds += zigbee.writeAttribute(0xFF01, 0x010B, 0x30, 0x0000) + // } + + state?.scale = getTemperatureScale() if(FloorMaxAirTemperatureParam){ @@ -288,12 +314,15 @@ def updated() { if(AuxiliaryCycleLengthParam){ switch (AuxiliaryCycleLengthParam) { + case "1": case "15 seconds": cmds += zigbee.writeAttribute(0x0201, 0x0404, 0x21, 0x000F)//15 sec break + case "2": case "30 minutes": cmds += zigbee.writeAttribute(0x0201, 0x0404, 0x21, 0x0708)//30min = 1800sec = 0x708 break + case "0": case "disable": default: cmds += zigbee.writeAttribute(0x0201, 0x0404, 0x21, 0x0000)//turn of the auxiliary @@ -313,35 +342,24 @@ def updated() { void initialize() { state?.scale = getTemperatureScale() - def supportedThermostatModes = ['off', 'heat'] state?.supportedThermostatModes = supportedThermostatModes - sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: (settings.trace ?: false)) + + configureSupportedRanges(); updated()//some thermostats values are not restored to a default value when disconnected. //executing the updated function make sure the thermostat parameters and the app parameters are in sync - if(state?.scale == 'C') - { - sendEvent(name: "heatingSetpointRange", value: [5.0, 36.0], scale: scale) - } - else if(state?.scale == 'F') - { - sendEvent(name: "heatingSetpointRangeLow", value: [41,96], scale: scale) - } - - sendEvent(name: "lock", value: "unlocked") - //for some reasons, the "runIn()" is not working in the "initialize()" of this driver. //to go around the problem, a read and a configuration is sent to each attribute required dor a good behaviour of the application def cmds = [] cmds += zigbee.readAttribute(0x0204, 0x0000) // Rd thermostat display mode if (state?.scale == 'C') { cmds += zigbee.writeAttribute(0x0204, 0x0000, 0x30, 0) // Wr °C on thermostat display - sendEvent(name: "heatingSetpointRange", value: [5,36], scale: state.scale) + sendEvent(name: "heatingSetpointRange", value: [5,36], scale: state?.scale) } else { cmds += zigbee.writeAttribute(0x0204, 0x0000, 0x30, 1) // Wr °F on thermostat display - sendEvent(name: "heatingSetpointRange", value: [41,96], scale: state.scale) + sendEvent(name: "heatingSetpointRange", value: [41,96], scale: state?.scale) } cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature cmds += zigbee.readAttribute(0x0201, 0x0012) // Rd thermostat Occupied heating setpoint @@ -362,12 +380,15 @@ void initialize() { def configure() { traceEvent(settings.logFilter, "Configuring Reporting and Bindings", settings.trace, get_LOG_DEBUG()) - //allow 30 min without receiving on/off report - return sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + //allow 5 min without receiving temperature report + return sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } def ping() { - refresh() + def cmds = []; + cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature + + sendZigbeeCommands(cmds) } def uninstalled() { @@ -405,10 +426,12 @@ def parse(String description) { def createCustomMap(descMap){ def result = null def map = [: ] - def scale = getTemperatureScale() + def scale = temperatureScale if (descMap.cluster == "0201" && descMap.attrId == "0000") { + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) map.name = "temperature" - map.value = getTemperatureValue(descMap.value, true) + map.value = getTemperatureValue(descMap.value, false) + map.unit = scale if(map.value > 158) {//if the value of the temperature is over 128C, it is considered an error with the temperature sensor map.value = "Sensor Error" @@ -426,13 +449,12 @@ def createCustomMap(descMap){ } } traceEvent(settings.logFilter, "parse>ACTUAL TEMP: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value, unit: scale) - sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + //allow 5 min without receiving temperature report + sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } else if (descMap.cluster == "0201" && descMap.attrId == "0008") { map.name = "heatingDemand" map.value = getHeatingDemand(descMap.value) - sendEvent(name: map.name, value: map.value) traceEvent(settings.logFilter, "parse>${map.name}: ${map.value}") def operatingState = (map.value.toInteger() < 10) ? "idle" : "heating" sendEvent(name: "thermostatOperatingState", value: operatingState) @@ -440,23 +462,18 @@ def createCustomMap(descMap){ } else if (descMap.cluster == "0201" && descMap.attrId == "0012") { + configureSupportedRanges(); + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) map.name = "heatingSetpoint" map.value = getTemperatureValue(descMap.value, true) - sendEvent(name: map.name, value: map.value, unit: scale) + map.unit = scale traceEvent(settings.logFilter, "parse>OCCUPY: ${map.name}: ${map.value}, scale: ${scale} ", settings.trace) } else if (descMap.cluster == "0201" && descMap.attrId == "001c") { map.name = "thermostatMode" map.value = getModeMap()[descMap.value] - sendEvent(name: map.name, value: map.value) traceEvent(settings.logFilter, "parse>${map.name}: ${map.value}", settings.trace) } - else if (descMap.cluster == "0204" && descMap.attrId == "0001") { - map.name = "keypadLockStatus" - map.value = getLockMap()[descMap.value] - traceEvent(settings.logFilter, "parse>KEYPAD LOCK STATUS: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value) - } else if (descMap.cluster == "FF01" && descMap.attrId == "010c") { map.name = "floorLimitStatus" if(descMap.value.toInteger() == 0){ @@ -470,10 +487,14 @@ def createCustomMap(descMap){ }else{ map.value = "floorAirLimitMaxReached" } + if(map.value != "OK"){ + log.warn map.value + } traceEvent(settings.logFilter, "parse>floorLimitStatus: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value) } - + if(map){ + result = createEvent(map); + } return result } @@ -556,7 +577,7 @@ def heatLevelDown() { } def setHeatingSetpoint(degrees) { - def scale = state?.scale + def scale = getTemperatureScale() degrees = checkTemperature(degrees) def degreesDouble = degrees as Double String tempValueString @@ -565,8 +586,6 @@ def setHeatingSetpoint(degrees) { } else { tempValueString = String.format('%2d', degreesDouble.intValue()) } - sendEvent("name": "heatingSetpoint", "value": tempValueString, scale: scale) - sendEvent("name": "thermostatSetpoint", "value": tempValueString, scale: scale) traceEvent(settings.logFilter, "setHeatingSetpoint> new setPoint: $tempValueString", settings.trace) def celsius = (scale == "C") ? degreesDouble : (fahrenheitToCelsius(degreesDouble) as Double).round(1) def cmds = [] @@ -603,19 +622,9 @@ def getModeMap() { ] } -def getSupportedThermostatModes() { - - if (!state?.supportedThermostatModes) { - state?.supportedThermostatModes = (device.currentValue("supportedThermostatModes")) ? - device.currentValue("supportedThermostatModes").toString().minus('[').minus(']').tokenize(',') : ['off', 'heat'] - } - return state?.supportedThermostatModes -} - def setThermostatMode(mode) { traceEvent(settings.logFilter, "setThermostatMode>switching thermostatMode", settings.trace) mode = mode?.toLowerCase() - def supportedThermostatModes = getSupportedThermostatModes() if (mode in supportedThermostatModes) { "mode_$mode" () @@ -626,22 +635,20 @@ def setThermostatMode(mode) { def mode_off() { traceEvent(settings.logFilter, "off>begin", settings.trace) - sendEvent(name: "thermostatMode", value: "off", data: [supportedThermostatModes: getSupportedThermostatModes()]) def cmds = [] cmds += zigbee.writeAttribute(0x0201, 0x001C, 0x30, 0) - cmds += zigbee.readAttribute(0x0201, 0x0008) - sendZigbeeCommands(cmds) + cmds += zigbee.readAttribute(0x0201, 0x001C) traceEvent(settings.logFilter, "off>end", settings.trace) + sendZigbeeCommands(cmds) } def mode_heat() { traceEvent(settings.logFilter, "heat>begin", settings.trace) - sendEvent(name: "thermostatMode", value: "heat", data: [supportedThermostatModes: getSupportedThermostatModes()]) def cmds = [] cmds += zigbee.writeAttribute(0x0201, 0x001C, 0x30, 4) - cmds += zigbee.readAttribute(0x0201, 0x0008) - sendZigbeeCommands(cmds) + cmds += zigbee.readAttribute(0x0201, 0x001C) traceEvent(settings.logFilter, "heat>end", settings.trace) + sendZigbeeCommands(cmds) } //-- Keypad Lock ----------------------------------------------------------------------------------------- @@ -656,51 +663,14 @@ def getLockMap() { ] } -def unlock() { - traceEvent(settings.logFilter, "unlock>begin", settings.trace) - sendEvent(name: "lock", value: "unlocked") - def cmds = [] - cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x00) - sendZigbeeCommands(cmds) - traceEvent(settings.logFilter, "unlock>end", settings.trace) -} - -def lock() { - traceEvent(settings.logFilter, "lock>begin", settings.trace) - sendEvent(name: "lock", value: "locked") - def cmds = [] - cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x01) - sendZigbeeCommands(cmds) - traceEvent(settings.logFilter, "lock>end", settings.trace) -} - def refresh() { - if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 20000) { // Check if last update > 20 sec + if (true || !state.updatedLastRanAt || now() >= state.updatedLastRanAt + 5000) { // Check if last update > 5 sec state.updatedLastRanAt = now() state?.scale = getTemperatureScale() traceEvent(settings.logFilter, "refresh>scale=${state.scale}", settings.trace) def cmds = [] - cmds += zigbee.readAttribute(0x0204, 0x0000) // Rd thermostat display mode - - def heatingSetpointRangeHigh - def heatingSetpointRangeLow - if(state?.scale == 'C') - { - heatingSetpointRangeLow = 5.0 - heatingSetpointRangeHigh = 36.0 - } - else if(state?.scale == 'F') - { - heatingSetpointRangeLow = 41 - heatingSetpointRangeHigh = 96 - } - def low = heatingSetpointRangeLow.toFloat().round(1) - def high = heatingSetpointRangeHigh.toFloat().round(1) - def heatingSetpointRange= [low,high] - sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) - cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature cmds += zigbee.readAttribute(0x0201, 0x0012) // Rd thermostat Occupied heating setpoint cmds += zigbee.readAttribute(0x0201, 0x0008) // Rd thermostat PI heating demand @@ -710,11 +680,6 @@ def refresh() { cmds += zigbee.readAttribute(0x0201, 0x0016) // Rd thermostat Maximum heating setpoint cmds += zigbee.readAttribute(0xFF01, 0x0105) // Rd thermostat Control mode - cmds += zigbee.configureReporting(0x0201, 0x0000, 0x29, 19, 300, 25) // local temperature - cmds += zigbee.configureReporting(0x0201, 0x0008, 0x0020, 11, 301, 10) // heating demand - cmds += zigbee.configureReporting(0x0201, 0x0012, 0x0029, 8, 302, 40) // occupied heating setpoint - cmds += zigbee.configureReporting(0xFF01, 0x010C, 0x30, 10, 3600, 1) // floor limit status each hours - sendZigbeeCommands(cmds) refresh_misc() } @@ -737,8 +702,11 @@ void refresh_misc() { int outdoorTempValue int outdoorTempToSend - if(!settings.DisableOutdorTemperatureParam) - { + if(DisableOutdorTemperatureParam == "Setpoint" || DisableOutdorTemperatureParam == "0") + {//delete outdoorTemp + cmds += zigbee.writeAttribute(0xFF01, 0x0010, 0x29, 0x8000) + } + else{ cmds += zigbee.writeAttribute(0xFF01, 0x0011, 0x21, 10800)//set the outdoor temperature timeout to 3 hours if (outdoorTemp < 0) { outdoorTempValue = -outdoorTemp*100 - 65536 @@ -753,12 +721,6 @@ void refresh_misc() { cmds += zigbee.writeAttribute(0xFF01, 0x0010, 0x29, outdoorTempToSend, [mfgCode: 0x119C]) } } - else - {//delete outdoorTemp - //the outdoor temperature cannot be directly erased from the thermostat. - //to erase it rapidly, the external temperature timeout must be set to the minimal value (30sec) - cmds += zigbee.writeAttribute(0xFF01, 0x0011, 0x21, 30)//set the outdoor temperature timeout to 30sec - } def mytimezone = location.getTimeZone() long dstSavings = 0 @@ -788,7 +750,7 @@ void refresh_misc() { //-- Private functions ----------------------------------------------------------------------------------- -void sendZigbeeCommands(cmds, delay = 1000) { +void sendZigbeeCommands(cmds, delay = 250) { cmds.removeAll { it.startsWith("delay") } // convert each command into a HubAction cmds = cmds.collect { new physicalgraph.device.HubAction(it) } @@ -882,35 +844,25 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } } \ No newline at end of file diff --git a/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy b/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy index 294800254aa..bedf7760734 100644 --- a/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy +++ b/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy @@ -1,7 +1,6 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1.3.0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -10,33 +9,34 @@ SVN-571 metadata { preferences { - input("BacklightAutoDimParam", "enum", title:"Backlight setting (Default: Sensing)", description: "On Demand or Sensing", options: ["Sensing", "On Demand"], multiple: false, required: false) - input("KbdLockParam", "enum", title: "Keypad lock (Default: Unlocked)", description: "Enable or disable the device's buttons.",options: ["Lock", "Unlock"], multiple: false, required: false) - input("TimeFormatParam", "enum", title:"Time Format (Default: 24h)", description: "Time format displayed by the device.", options:["24h", "12h AM/PM"], multiple: false, required: false) - input("DisableOutdorTemperatureParam", "bool", title: "enable/disable outdoor temperature", description: "Set it to true to Disable outdoor temperature on the thermostat") + input("BacklightAutoDimParam", "enum", title:"Backlight setting (Default: Always ON)", + description: "On Demand or Always ON", options: ["On Demand", "Always ON"], multiple: false, required: false) + input("KbdLockParam", "enum", title: "Keypad lock (Default: Unlocked)", + description: "Enable or disable the device's buttons.",options: ["Lock", "Unlock"], multiple: false, required: false) + input("TimeFormatParam", "enum", title:"Time Format (Default: 24h)", + description: "Time format displayed by the device.", options:["12h AM/PM", "24h"], multiple: false, required: false) + input("DisableOutdorTemperatureParam", "enum", title: "Secondary display (Default: Outside temp.)", multiple: false, required: false, options: ["Setpoint", "Outside temp."], + description: "Information displayed in the secondary zone of the device") input("trace", "bool", title: "Trace (Only for debugging)", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") } definition(name: "TH1500ZB Sinope Thermostat", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.thermostat") { - capability "thermostatHeatingSetpoint" - capability "thermostatMode" - capability "thermostatOperatingState" - capability "thermostatSetpoint" - capability "Actuator" capability "Temperature Measurement" + capability "Thermostat" + capability "Thermostat Heating Setpoint" + capability "Thermostat Mode" + capability "Thermostat Operating State" + capability "Actuator" capability "Configuration" + capability "Health check" capability "Refresh" capability "Sensor" - capability "lock" - attribute "temperatureDisplayMode", "enum", ["Deg_C", "Deg_F"] - attribute "occupancyStatus", "enum", ["unoccupy", "occupy"] attribute "outdoorTemp", "string" attribute "heatingSetpointRange", "VECTOR3" - attribute "verboseTrace", "string" command "heatLevelUp" command "heatLevelDown" @@ -55,7 +55,7 @@ preferences { attributeState("VALUE_DOWN", action: "heatLevelDown") } tileAttribute("device.heatingDemand", key: "SECONDARY_CONTROL") { - attributeState("default", label: '${currentValue}%', unit: "%") + attributeState("default", label: '${currentValue}%', unit: "%", icon:"st.Weather.weather2") } tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") { attributeState("idle", backgroundColor: "#44b621") @@ -88,44 +88,28 @@ preferences { //-- Main & Details ---------------------------------------------------------------------------------------- main("thermostatMulti") - details(["thermostatMulti", - "heatingSetpointSlider", - "thermostatMode", - "refresh" - ]) + details(["thermostatMulti", "heatingSetpointSlider", "thermostatMode", "refresh"]) } } -def getBackgroundColors() { - def results - if (state?.scale == 'C') { - // Celsius Color Range - results = [ - [value: 0, color: "#153591"], - [value: 7, color: "#1e9cbb"], - [value: 15, color: "#90d2a7"], - [value: 23, color: "#44b621"], - [value: 29, color: "#f1d801"], - [value: 35, color: "#d04e00"], - [value: 37, color: "#bc2323"] - ] - } else { - results = - // Fahrenheit Color Range - [ - [value: 31, color: "#153591"], - [value: 44, color: "#1e9cbb"], - [value: 59, color: "#90d2a7"], - [value: 74, color: "#44b621"], - [value: 84, color: "#f1d801"], - [value: 95, color: "#d04e00"], - [value: 96, color: "#bc2323"] - ] - } - return results - +def getThermostatSetpointRange() { + (getTemperatureScale() == "C") ? [5, 36] : [41, 96] +} + +def getHeatingSetpointRange() { + thermostatSetpointRange } +def getSupportedThermostatModes() { + ["heat", "off"] +} + +def configureSupportedRanges() { + sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: false) + // These are part of the deprecated Thermostat capability. Remove these when that capability is removed. + sendEvent(name: "thermostatSetpointRange", value: thermostatSetpointRange, displayed: false) + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, displayed: false) +} //-- Installation ---------------------------------------------------------------------------------------- @@ -138,7 +122,7 @@ def installed() { def updated() { - if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 15000) { + if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 1000) { state.updatedLastRanAt = now() def cmds = [] @@ -151,16 +135,16 @@ def updated() { runEvery15Minutes(refresh_misc) - if(KbdLockParam == "Lock"){ + if(KbdLockParam == "Lock" || KbdLockParam == '0'){ traceEvent(settings.logFilter,"device lock",settings.trace) - lock() + cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x01) } else{ traceEvent(settings.logFilter,"device unlock",settings.trace) - unlock() + cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x00) } - if(TimeFormatParam == "12h AM/PM"){//12h AM/PM + if(TimeFormatParam == "12h AM/PM" || TimeFormatParam == '0'){//12h AM/PM traceEvent(settings.logFilter,"Set to 12h AM/PM",settings.trace) cmds += zigbee.writeAttribute(0xFF01, 0x0114, 0x30, 0x0001) } @@ -187,23 +171,13 @@ def updated() { void initialize() { state?.scale = getTemperatureScale() - def supportedThermostatModes = ['off', 'heat'] state?.supportedThermostatModes = supportedThermostatModes - sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: (settings.trace ?: false)) + + configureSupportedRanges(); updated()//some thermostats values are not restored to a default value when disconnected. //executing the updated function make sure the thermostat parameters and the app parameters are in sync - if(state?.scale == 'C') - { - sendEvent(name: "heatingSetpointRange", value: [5.0, 36.0], scale: scale) - } - else if(state?.scale == 'F') - { - sendEvent(name: "heatingSetpointRangeLow", value: [41,96], scale: scale) - } - - sendEvent(name: "lock", value: "unlocked") //for some reasons, the "runIn()" is not working in the "initialize()" of this driver. //to go around the problem, a read and a configuration is sent to each attribute required dor a good behaviour of the application @@ -221,7 +195,6 @@ void initialize() { cmds += zigbee.readAttribute(0x0201, 0x0012) // Rd thermostat Occupied heating setpoint cmds += zigbee.readAttribute(0x0201, 0x0008) // Rd thermostat PI heating demand cmds += zigbee.readAttribute(0x0201, 0x001C) // Rd thermostat System Mode - cmds += zigbee.readAttribute(0x0204, 0x0001) // Rd thermostat Keypad lockout cmds += zigbee.readAttribute(0xFF01, 0x0105) // Rd thermostat Control mode cmds += zigbee.configureReporting(0x0201, 0x0000, 0x29, 19, 300, 25) // local temperature @@ -240,7 +213,9 @@ def configure() } def ping() { - refresh() + def cmds = []; + cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature + sendZigbeeCommands(cmds) } def uninstalled() { @@ -278,10 +253,12 @@ def parse(String description) { def createCustomMap(descMap){ def result = null def map = [: ] - def scale = getTemperatureScale() + def scale = temperatureScale if (descMap.cluster == "0201" && descMap.attrId == "0000") { + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) map.name = "temperature" - map.value = getTemperatureValue(descMap.value, true) + map.value = getTemperatureValue(descMap.value, false) + map.unit = scale if(map.value > 158) {//if the value of the temperature is over 128C, it is considered an error with the temperature sensor map.value = "Sensor Error" @@ -297,16 +274,14 @@ def createCustomMap(descMap){ { map.value = String.format( "%d", map.value ) } - } traceEvent(settings.logFilter, "parse>ACTUAL TEMP: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value, unit: scale) - sendEvent(name: "checkInterval", value: 30*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + //allow 5 min without receiving temperature report + sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } else if (descMap.cluster == "0201" && descMap.attrId == "0008") { map.name = "heatingDemand" map.value = getHeatingDemand(descMap.value) - sendEvent(name: map.name, value: map.value) traceEvent(settings.logFilter, "parse>${map.name}: ${map.value}") def operatingState = (map.value.toInteger() < 10) ? "idle" : "heating" sendEvent(name: "thermostatOperatingState", value: operatingState) @@ -314,23 +289,21 @@ def createCustomMap(descMap){ } else if (descMap.cluster == "0201" && descMap.attrId == "0012") { + configureSupportedRanges(); + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) map.name = "heatingSetpoint" map.value = getTemperatureValue(descMap.value, true) - sendEvent(name: map.name, value: map.value, unit: scale) + map.unit = scale traceEvent(settings.logFilter, "parse>OCCUPY: ${map.name}: ${map.value}, scale: ${scale} ", settings.trace) } else if (descMap.cluster == "0201" && descMap.attrId == "001c") { map.name = "thermostatMode" map.value = getModeMap()[descMap.value] - sendEvent(name: map.name, value: map.value) traceEvent(settings.logFilter, "parse>${map.name}: ${map.value}", settings.trace) } - else if (descMap.cluster == "0204" && descMap.attrId == "0001") { - map.name = "keypadLockStatus" - map.value = getLockMap()[descMap.value] - traceEvent(settings.logFilter, "parse>KEYPAD LOCK STATUS: ${map.value}", settings.trace) - sendEvent(name: map.name, value: map.value) - } + if(map){ + result = createEvent(map); + } return result } @@ -427,8 +400,6 @@ def setHeatingSetpoint(degrees) { } else { tempValueString = String.format('%2d', degreesDouble.intValue()) } - sendEvent("name": "heatingSetpoint", "value": tempValueString, scale: scale) - sendEvent("name": "thermostatSetpoint", "value": tempValueString, scale: scale) traceEvent(settings.logFilter, "setHeatingSetpoint> new setPoint: $tempValueString", settings.trace) def celsius = (scale == "C") ? degreesDouble : (fahrenheitToCelsius(degreesDouble) as Double).round(1) def cmds = [] @@ -465,19 +436,9 @@ def getModeMap() { ] } -def getSupportedThermostatModes() { - - if (!state?.supportedThermostatModes) { - state?.supportedThermostatModes = (device.currentValue("supportedThermostatModes")) ? - device.currentValue("supportedThermostatModes").toString().minus('[').minus(']').tokenize(',') : ['off', 'heat'] - } - return state?.supportedThermostatModes -} - def setThermostatMode(mode) { traceEvent(settings.logFilter, "setThermostatMode>switching thermostatMode", settings.trace) mode = mode?.toLowerCase() - def supportedThermostatModes = getSupportedThermostatModes() if (mode in supportedThermostatModes) { "mode_$mode" () @@ -488,94 +449,38 @@ def setThermostatMode(mode) { def mode_off() { traceEvent(settings.logFilter, "off>begin", settings.trace) - sendEvent(name: "thermostatMode", value: "off", data: [supportedThermostatModes: getSupportedThermostatModes()]) def cmds = [] cmds += zigbee.writeAttribute(0x0201, 0x001C, 0x30, 0) - cmds += zigbee.readAttribute(0x0201, 0x0008) - sendZigbeeCommands(cmds) + cmds += zigbee.readAttribute(0x0201, 0x001C) traceEvent(settings.logFilter, "off>end", settings.trace) + sendZigbeeCommands(cmds) } def mode_heat() { traceEvent(settings.logFilter, "heat>begin", settings.trace) - sendEvent(name: "thermostatMode", value: "heat", data: [supportedThermostatModes: getSupportedThermostatModes()]) def cmds = [] cmds += zigbee.writeAttribute(0x0201, 0x001C, 0x30, 4) - cmds += zigbee.readAttribute(0x0201, 0x0008) - sendZigbeeCommands(cmds) + cmds += zigbee.readAttribute(0x0201, 0x001C) traceEvent(settings.logFilter, "heat>end", settings.trace) -} -//-- Keypad Lock ----------------------------------------------------------------------------------------- - -def keypadLockLevel() { - ["unlock", "lock"] //only those level are used for the moment -} - -def getLockMap() { - [ - "00": "unlocked", - "01": "locked", - ] -} - -def unlock() { - traceEvent(settings.logFilter, "unlock>begin", settings.trace) - sendEvent(name: "lock", value: "unlocked") - def cmds = [] - cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x00) sendZigbeeCommands(cmds) - traceEvent(settings.logFilter, "unlock>end", settings.trace) -} - -def lock() { - traceEvent(settings.logFilter, "lock>begin", settings.trace) - sendEvent(name: "lock", value: "locked") - def cmds = [] - cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x01) - sendZigbeeCommands(cmds) - traceEvent(settings.logFilter, "lock>end", settings.trace) } def refresh() { - if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 20000) { // Check if last update > 20 sec + if (true || !state.updatedLastRanAt || now() >= state.updatedLastRanAt + 5000) { // Check if last update > 5 sec state.updatedLastRanAt = now() state?.scale = getTemperatureScale() traceEvent(settings.logFilter, "refresh>scale=${state.scale}", settings.trace) def cmds = [] - cmds += zigbee.readAttribute(0x0204, 0x0000) // Rd thermostat display mode - - def heatingSetpointRangeHigh - def heatingSetpointRangeLow - if(state?.scale == 'C') - { - heatingSetpointRangeLow = 5.0 - heatingSetpointRangeHigh = 36.0 - } - else if(state?.scale == 'F') - { - heatingSetpointRangeLow = 41 - heatingSetpointRangeHigh = 96 - } - def low = heatingSetpointRangeLow.toFloat().round(1) - def high = heatingSetpointRangeHigh.toFloat().round(1) - def heatingSetpointRange= [low,high] - sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, scale: state.scale) - cmds += zigbee.readAttribute(0x0201, 0x0000) // Rd thermostat Local temperature cmds += zigbee.readAttribute(0x0201, 0x0012) // Rd thermostat Occupied heating setpoint cmds += zigbee.readAttribute(0x0201, 0x0008) // Rd thermostat PI heating demand cmds += zigbee.readAttribute(0x0201, 0x001C) // Rd thermostat System Mode - cmds += zigbee.readAttribute(0x0204, 0x0001) // Rd thermostat Keypad lockout cmds += zigbee.readAttribute(0x0201, 0x0015) // Rd thermostat Minimum heating setpoint cmds += zigbee.readAttribute(0x0201, 0x0016) // Rd thermostat Maximum heating setpoint cmds += zigbee.readAttribute(0xFF01, 0x0105) // Rd thermostat Control mode - cmds += zigbee.configureReporting(0x0201, 0x0000, 0x29, 19, 300, 25) // local temperature - cmds += zigbee.configureReporting(0x0201, 0x0008, 0x0020, 11, 301, 10) // heating demand - cmds += zigbee.configureReporting(0x0201, 0x0012, 0x0029, 8, 302, 40) // occupied heating setpoint - sendZigbeeCommands(cmds) refresh_misc() } @@ -598,8 +503,10 @@ void refresh_misc() { int outdoorTempValue int outdoorTempToSend - if(!settings.DisableOutdorTemperatureParam) - { + if(DisableOutdorTemperatureParam == "Setpoint" || DisableOutdorTemperatureParam == "0"){//delete outdoorTemp + cmds += zigbee.writeAttribute(0xFF01, 0x0010, 0x29, 0x8000) + } + else{ cmds += zigbee.writeAttribute(0xFF01, 0x0011, 0x21, 10800)//set the outdoor temperature timeout to 3 hours if (outdoorTemp < 0) { outdoorTempValue = -outdoorTemp*100 - 65536 @@ -614,12 +521,6 @@ void refresh_misc() { cmds += zigbee.writeAttribute(0xFF01, 0x0010, 0x29, outdoorTempToSend, [mfgCode: 0x119C]) } } - else - {//delete outdoorTemp - //the outdoor temperature cannot be directly erased from the thermostat. - //to erase it rapidly, the external temperature timeout must be set to the minimal value (30sec) - cmds += zigbee.writeAttribute(0xFF01, 0x0011, 0x21, 30)//set the outdoor temperature timeout to 30sec - } def mytimezone = location.getTimeZone() long dstSavings = 0 @@ -649,7 +550,7 @@ void refresh_misc() { //-- Private functions ----------------------------------------------------------------------------------- -void sendZigbeeCommands(cmds, delay = 1000) { +void sendZigbeeCommands(cmds, delay = 250) { cmds.removeAll { it.startsWith("delay") } // convert each command into a HubAction cmds = cmds.collect { new physicalgraph.device.HubAction(it) } @@ -743,35 +644,25 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } } \ No newline at end of file diff --git a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy index 22e9928ecc1..da0602ea1cd 100644 --- a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy +++ b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy @@ -1,7 +1,6 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1..0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,8 +12,8 @@ metadata { preferences { input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") } definition (name: "VA4200WZ-VA4200ZB Sinope Valve", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.watervalve") { @@ -292,35 +291,25 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } } \ No newline at end of file diff --git a/devicetypes/sinope-technologies/wl4200s-wl4200-sinope-water-leak-sensor.src/wl4200s-wl4200-sinope-water-leak-sensor.groovy b/devicetypes/sinope-technologies/wl4200s-wl4200-sinope-water-leak-sensor.src/wl4200s-wl4200-sinope-water-leak-sensor.groovy index f28056c9d3e..1cd25862e10 100644 --- a/devicetypes/sinope-technologies/wl4200s-wl4200-sinope-water-leak-sensor.src/wl4200s-wl4200-sinope-water-leak-sensor.groovy +++ b/devicetypes/sinope-technologies/wl4200s-wl4200-sinope-water-leak-sensor.src/wl4200s-wl4200-sinope-water-leak-sensor.groovy @@ -1,7 +1,6 @@ /** - -Copyright Sinopé Technologies 2019 -1.1.0 +Copyright Sinopé Technologies +1.3.0 SVN-571 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -12,8 +11,8 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus preferences { section { input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") - input("logFilter", "number", title: "Trace level", range: "1..5", - description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") } } @@ -320,36 +319,26 @@ def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMes int LOG_INFO = get_LOG_INFO() int LOG_DEBUG = get_LOG_DEBUG() int LOG_TRACE = get_LOG_TRACE() - int filterLevel = (logFilter) ? logFilter.toInteger() : get_LOG_WARN() - - if ((displayEvent) || (sendMessage)) { - def results = [ - name: "verboseTrace", - value: message, - displayed: ((displayEvent) ?: false) - ] - - if ((displayEvent) && (filterLevel >= traceLevel)) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } /* end switch*/ - if (sendMessage) sendEvent(results) - } /* end if displayEvent*/ + + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } } }