Aeotec aërQ – mały czujnik temperatury i wilgotności

Dzisiaj przyjrzymy się czujnikowi temperatury i wilgotności od firmy Aeotec o bardzo wdzięcznej nazwie Aeotec aërQ Temperature & Humidity Sensor.

Aeotec aërQ to, jak podaje producent, najmniejszy czujnik temperatury i wilgotności działający w standardzie Z-wave. Sensor dodatkowo mierzy punkt rosy oraz ocenia ryzyko powstawania pleśni, przekazując tę informację do centrali.

Recenzja powstała dzięki uprzejmości sklepu Z-Home.pl, który udostępnił czujnik do testów.

Opis

Główną funkcją jest czujnika jest pomiar parametrów: temperatury (-10 do 65°C (+- 1°C)), wilgotności (0 do 80% RH +- 3%) oraz punkt rosy -10 do 65°C (+- 1°C), czyli temperatury, w której może rozpocząć się proces skraplania pary wodnej. W ekosystemie SmartThings mierzy parametry co 15 minut, a po kliknięciu przycisku natychmiastowo aktualizuje stan. Dodatkowo w tym momencie czujnik sygnalizuje diodą LED – zielony kolor oznacza odpowiednie warunki, czerwony ostrzeżenie przed możliwością powstawania pleśni. Komunikacja odbywa się po protokole Z-wave. Działa dzięki zasilaniu bateryjnemu informując o aktualnym zużyciu. Bateria to CR2477 koszt to około 15 zł za sztukę, przy fabrycznych ustawieniach czułości mowa o 2 latach pracy na jednej baterii. Sensor ma bardzo małe rozmiary: 35 x 35 x 18 mm, a waga to zaledwie 21 gramów. Czujnik posiada klasę szczelności IP20, czyli odporności na kurz. Nie powinniśmy stawiać czujnika w bezpośrednim sąsiedztwie z wodą. Aeotec aërQ może pracować w temperaturze 0°C do 45°C oraz wilgotności w zakresie 10 do 95%. Dostępny tylko w kolorze białym.

Przykładowe wykorzystanie to proste otrzymanie powiadomienia o zaparowanej łazience i konieczności przewietrzenia pomieszczenia.

  • Plusy
    • Mały rozmiar
    • Wartość punktu rosy
  • Minusy
    • Przetwarzanie w chmurze
    • Cena

Współpraca z SmartThings

W głównym panelu aplikacji SmartThings mamy informację o zmierzonej temperaturze i wilgotności. Dodatkowo mamy parametr „Dewpoint Measurement”, czyli punkt rosy. Ostatnią informacją jest procentowy poziom baterii. Panel „Historii” nie różni się znacznie od innych urządzeń, mamy dostęp do sortowania i przestudiowania historii działań sensora z ostatnich 7 dni. W ustawieniach „Edytuj” mamy standardową możliwość zmiany nazwy, lokalizacji oraz pokoju. W dodatkowych ustawieniach posiadamy możliwość otrzymania powiadomienia o minimalnej temperaturze (w zakresie 1-100°C) oraz minimalnej wilgotności (w zakresie 1-20%). Jak często otrzymujemy raport, możliwość zmiany ze stopni Celsjusza na stopnie Fahrenheita oraz możliwość przesunięcia wartości mierzonej temperatury i wilgotności.

Urządzenia w ekosystemie SmartThings wykonuje przetwarzanie poleceń w chmurze, z tego też tytułu przy utracie połączenia z Internetem, nie jest możliwe raportowanie, aktualizowanie stanów urządzeń, jak i zaprogramowanych zadań np. automatyzacji.

Przy tworzeniu automatyzacji „Jeśli … to Wtedy” ze względu na swoją specyfikę urządzenia. Możliwe jest tylko wykorzystanie warunku „jeśli”.  Czujnik aërQ pozwala nam na wybranie opcji / wysłanie powiadomienia o temperaturze z zakresu -20 – 50°C w tym czy są większe/mniejsze niż podana. Również powiadomi o stanie wilgotności z zakresu 0 -100% w tym czy jest większa/mniejsza. Poziom punktu rosy. Tutaj bez zakresu, oczywiście w tym czy jest większa/mniejsza. Ostatnia informacja to stan baterii z zakresu 0 – 100% w tym czy jest większa/mniejsza.

Instalacja

Przed procesem dodania sensora konieczne jest zainstalowanie niestandardowego programu obsługi. Oryginalny kod obsługi dostępny jest tutaj. Poniżej zamieszczam, kod ze spolszczonymi ustawieniami czujnika aërQ. Dodatkowo należy otworzyć tylnie plecki sensora i usunąć zaślepkę baterii.

/**
 *  Copyright 2018 SmartThings
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.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. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  Z-Wave Temp/Humidity Sensor
 *
 *  Author: Chris
 *  Date: 2021-01-29
 */
metadata {
	definition(name: "Aeotec aerQ Sensor", namespace: "aeotec", author: "Aeotec", ocfDeviceType: "oic.d.thermostat", executeCommandsLocally: false, mnmn: "0ALy", vid: "f680ed17-d31d-3aef-a888-1a7ffbceb1cc") {
		capability "Sensor"
		capability "Battery"
		capability "Health Check"
		capability "Temperature Measurement"
		capability "Relative Humidity Measurement"
		capability "Configuration"
        	capability "forgottenreturn26666.dewpointMeasurement"
        
		attribute "dewpoint", "number"
		attribute "updateNeeded", "string"
		attribute "parameter1", "number"
		attribute "parameter2", "number"
		attribute "parameter4", "number"
        	attribute "parameter64", "number"
        
		fingerprint mfr:"0371", prod:"0002", model:"0009", deviceJoinName: "Temperature Humidity Sensor", mnmn: "0ALy", vid: "f680ed17-d31d-3aef-a888-1a7ffbceb1cc" //EU //aerQ Sensor
		fingerprint mfr:"0371", prod:"0102", model:"0009", deviceJoinName: "Temperature Humidity Sensor", mnmn: "0ALy", vid: "f680ed17-d31d-3aef-a888-1a7ffbceb1cc" //US //aerQ Sensor
	}
	simulator {
		for (int i = 0; i <= 100; i += 20) {
			status "temperature ${i}F": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
				new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
					scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1)
			).incomingMessage()
		}
		for (int i = 0; i <= 100; i += 20) {
			status "humidity ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
				new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: i, sensorType: 5)
			).incomingMessage()
		}
		for (int i in [0, 5, 10, 15, 50, 99, 100]) {
			status "battery ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
				new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i)
			).incomingMessage()
		}
		status "low battery alert": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate(
			new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 255)
		).incomingMessage()
		status "wake up": "command: 8407, payload: "
	}
	tiles(scale: 2) {
		multiAttributeTile(name:"temperature", type:"generic", width:6, height:4, canChangeIcon: true) {
			tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
				attributeState("temperature", label: '${currentValue}°', 
                    			backgroundColors: [
					    // Celsius
					    [value: 0, color: "#153591"],
					    [value: 7, color: "#1e9cbb"],
					    [value: 15, color: "#90d2a7"],
					    [value: 23, color: "#44b621"],
					    [value: 28, color: "#f1d801"],
					    [value: 35, color: "#d04e00"],
					    [value: 37, color: "#bc2323"],
					    // Fahrenheit
					    [value: 40, 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"]
                    			]
				)
			}
        	}
        
        	valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
				state "humidity", label: '${currentValue}% humidity', unit: "%"
		}
        
        	valueTile("dewpoint", "device.dewpoint", inactiveLabel: false, width: 2, height: 2) {
			state "dewpoint", label: '${currentValue}°',
				backgroundColors: [
					[value: 0, color: "#153591"],
					[value: 7, color: "#1e9cbb"],
					[value: 15, color: "#90d2a7"],
					[value: 23, color: "#44b621"],
					[value: 28, color: "#f1d801"],
					[value: 35, color: "#d04e00"],
					[value: 37, color: "#bc2323"],
					// Fahrenheit
					[value: 40, 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"]
				]
		}
        
        	valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "battery", label: '${currentValue}% battery', unit: ""
		}
		main "temperature"
		details(["temperature", "humidity", "dewpoint", "battery"])
	}
        
	preferences {
		section {
			input(
				title: "Ustawienia progowe - te ustawienia są sprawdzane co 15 minut, jeśli zmiana temperatury lub wilgotności wystarcza do wysłania raportu. Działa jednocześnie z raportami okresowymi.",
				type: "paragraph",
				element: "paragraph"
			)
			input(
				title: "1. Wartość raportu minimalnej temperatury:",
				description: "Minimalna wymagana zmiana temperatury do wywołania raportu (wartość wynosi 1/10).",
				name: "thresholdTemperatureValue",
				type: "number",
				range: "1..100",
				defaultValue: 20
			)
            		input(
				title: "2. Wartość raportu minimalnej wilgotności:",
				description: "Minimalna wymagana zmiana temperatury do wywołania raportu.",
				name: "thresholdHumidityValue",
				type: "number",
				range: "1..20",
				defaultValue: 5
			)  
			input(
				title: "Ustawienie okresowe — określa, jak często zgłaszana jest zarówno temperatura, jak i wilgotność. To ustawienie działa w tym samym czasie co raporty progowe.",
				type: "paragraph",
				element: "paragraph"
			)
			input(
				title: "4. Raport okresowy:",
				description: "Określa, jak często temperatura i wilgotność są zgłaszane bez konieczności sprawdzania.",
				name: "periodicReportValue",
				type: "number",
				range: "900..65535",
				defaultValue: 43200
			)
            		input(
				title: "Ustawienie skali temperatury - ustawienie to zajmie 1 budzenie, aby ustawić się prawidłowo, a następnie następujący raport czujnika temperatury po tym wybudzeniu odpowiednio zmieni jednostkę temperatury i wartość. Jeśli chcesz zobaczyć natychmiastowe zmiany, obudź kilka razy aerQ Sensor.",
				type: "paragraph",
				element: "paragraph"
			)
            		input(
				title: "64. Skala temperatury:",
				description: "Ustaw raport jednostek temperatury w stopniach  Celsjusza lub Fahrenheita (1 = Celsjusz, 2 = Fahrenheit)",
				name: "temperatureScaleSetting",
				type: "number",
				range: "1..2",
			)
			input(
				title: "KONIEC PARAMETRÓW - Wszystkie konfiguracje będą miały miejsce po obudzeniu czujnika aerQ. Możesz poczekać do 12 godzin lub natychmiast obudzić aerQ, dotykając jego przycisku.",
				type: "paragraph",
				element: "paragraph"
			)
            		input(
				title: "Przesunięcie wartości czujnika - Użyj poniższych ustawień, aby przesunąć wartość wilgotności lub temperatury. Wartości zmienią się po następnym raporcie temperatury lub wilgotności.",
				type: "paragraph",
				element: "paragraph"
			)
            		input(
				title: "a. Przesunięcie wartości temperatury:",
				description: "Przesunięcie podanej wartości temperatury aerQ.",
				name: "offsetTemperature",
				type: "decimal",
				defaultValue: 0
			)
            		input(
				title: "b. Przesunięcie wartości wilgotności:",
				description: "Przesunięcie podanej wartości wilgotności aerQ.",
				name: "offsetHumidity",
				type: "number",
				defaultValue: 0
			)
		}
	}
}
def installed() {
	setCheckInterval()
	def cmds = [
		secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x0B)),
		secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01)),
		secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x05)),
		secure(zwave.batteryV1.batteryGet())
	]
	response(delayBetween(cmds, 1500))
}
def updated() {
	def cmds = []
	cmds << sendEvent(name: "updateNeeded", value: "true", displayed: false) 
	response(cmds)
}
private setCheckInterval() {
	sendEvent(name: "checkInterval", value: (2 * 12 + 2) * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
private getCommandClassVersions() {
	[0x20: 1, 0x30: 1, 0x31: 5, 0x80: 1, 0x84: 1, 0x70: 2, 0x71: 3, 0x9C: 1]
}
def parse(String description) {
	def result = null
	if (description.startsWith("Err")) {
		result = createEvent(descriptionText: description)
	} else {
		def cmd = zwave.parse(description, commandClassVersions)
		if (cmd) {
			result = zwaveEvent(cmd)
		} else {
			result = createEvent(value: description, descriptionText: description, isStateChange: false)
		}
	}
	log.debug "Parsed '$description' to $result"
	return result
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
	def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)]
    
	if (device.currentValue("updateNeeded") == "true") {
		if (thresholdTemperatureValue != state.parameter1 && thresholdTemperatureValue) {
			result << response(secure(zwave.configurationV1.configurationSet(parameterNumber: 1, size: 1, scaledConfigurationValue: thresholdTemperatureValue)))
			result << response(secure(zwave.configurationV1.configurationGet(parameterNumber: 1)))
		}
		if (thresholdHumidityValue != state.parameter2 && thresholdHumidityValue) {
			result << response(secure(zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, scaledConfigurationValue: thresholdHumidityValue)))
			result << response(secure(zwave.configurationV1.configurationGet(parameterNumber: 2)))
		}
		if (periodicReportValue != state.parameter4 && periodicReportValue) {
			result << response(secure(zwave.configurationV1.configurationSet(parameterNumber: 4, size: 2, scaledConfigurationValue: periodicReportValue)))
			result << response(secure(zwave.configurationV1.configurationGet(parameterNumber: 4)))
		}  
        	if (temperatureScaleSetting != state.parameter64 && temperatureScaleSetting) {
        		result << response(secure(zwave.configurationV1.configurationSet(parameterNumber: 64, size: 1, scaledConfigurationValue: temperatureScaleSetting)))
			result << response(secure(zwave.configurationV1.configurationGet(parameterNumber: 64)))
        	}
	}
	if (!state.lastbat || (new Date().time) - state.lastbat > 53 * 60 * 60 * 1000) {
		result << response(secure(zwave.batteryV1.batteryGet()))
	}
	result << response(secure(zwave.wakeUpV1.wakeUpNoMoreInformation()))
	result
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
	def map = [name: "battery", unit: "%"]
	if (cmd.batteryLevel == 0xFF) {
		map.value = 1
		map.descriptionText = "${device.displayName} has a low battery"
		map.isStateChange = true
	} else {
		map.value = cmd.batteryLevel
	}
	state.lastbat = new Date().time
	[createEvent(map), response(secure(zwave.wakeUpV1.wakeUpNoMoreInformation()))]
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
	def map = [:]
	switch (cmd.sensorType) {
		case 0x01:
			def finalTempValue 
			if (offsetTemperature){
				finalTempValue = cmd.scaledSensorValue
				finalTempValue = finalTempValue + offsetTemperature
			} else {
				finalTempValue = cmd.scaledSensorValue
			}
			map.name = "temperature"
			map.value = finalTempValue
			map.unit = cmd.scale == 1 ? "F" : "C"
		break;
		case 0x05:
			def finalHumValue
			if (offsetHumidity) {
				finalHumValue = cmd.scaledSensorValue.toInteger() + offsetHumidity.toInteger()
			} else {
				finalHumValue = cmd.scaledSensorValue.toInteger()
			}
			map.name = "humidity"
			map.value = finalHumValue
			map.unit = "%"
			break
		case 0x0B:
			map.name = "dewpoint"
			map.value = cmd.scaledSensorValue
			map.unit = cmd.scale == 1 ? "F" : "C"
			break
		default:
			map.descriptionText = cmd.toString()
			break
	}
	createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) {
	switch (cmd.parameterNumber) {
		case 0x01:
			state.parameter1 = cmd.scaledConfigurationValue
			sendEvent(name: "parameter1", value: cmd.scaledConfigurationValue, displayed: false) 
			break
		case 0x02:
			state.parameter2 = cmd.scaledConfigurationValue
			sendEvent(name: "parameter2", value: cmd.scaledConfigurationValue, displayed: false) 
			break
		case 0x04:
			state.parameter4 = cmd.scaledConfigurationValue
			if(state.parameter4 < 0) { 
				state.parameter4 = state.parameter4 + 65536 
			}
			sendEvent(name: "parameter4", value: state.parameter4, displayed: false) 
			break
		case 0x40:
			state.parameter64 = cmd.scaledConfigurationValue
			sendEvent(name: "parameter64", value: state.parameter64, displayed: false) 
			break
		default:
			log.debug "Setting unknown parameter"
			break
	}
    
	checkParameterValues()
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
	log.warn "Unhandled command: ${cmd}"
	createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
}
def checkParameterValues() {
	//jeśli ustawienia parametrów w jakiś sposób zawiodą, wybudzenie może spowodować, że ustawienia parametrów zostaną zaktualizowane ponownie następnym razem. Gdy wszystkie ustawienia są prawdziwe, następnym razem zatrzymaj aktualizację parametrów. 
	if (state.parameter1 == thresholdTemperatureValue && state.parameter2 == thresholdHumidityValue && state.parameter4 == periodicReportValue && state.parameter64 == temperatureScaleSetting) {
		sendEvent(name: "updateNeeded", value: "false", displayed: false) 
	} 
}
private secure(cmd) {
	if (zwaveInfo.zw.contains("s")) {
		zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
	} else {
		cmd.format()
	}
}

Powyższy kod w całości skopiuj do schowka. Następnie:

  1. Zaloguj się do IDE na https://account.smartthings.com.
  2. Kliknij „My Device Handlers”.
  3. Kliknij „+ Create New Device Handler” w prawym górnym rogu.
  4. Kliknij „From Code”.
  5. Wklej skopiowany kod (Ctrl + V) do edytora i kliknij „Create”.
  6. Kliknij „Publish”, a następnie „For Me” w prawym górnym rogu.

Szczegółową instrukcję instalacji niestandardowego programu obsługi wraz ze screenami można znaleźć we wcześniejszym wpisie.

Aby dodać urządzenie do inteligentnego domu. Należy na ekranie głównym mobilnej aplikacji SmartThings, kliknąć + w prawym górnym rogu. Kolejno „Dodaj urządzenie” i „Szukaj w pobliżu”. Teraz klikamy szybko 3 razy przycisk na czujniku aërQ. Po chwili centrala SmartThings znajdzie nasz nowy sensor. Zostaniemy poproszeni o wybór pokoju. Na koniec konieczne jest zeskanowanie/podanie kodu SmartStart. Znajdziemy go we wnętrzu urządzenia po zdjęciu tylnej obudowy lub w opakowaniu. Po pomyślnej weryfikacji, pozostaje już tylko nadać nazwę sensorowi i kliknąć „Gotowe”.

Jeśli pojawią sie problemy i urządzenie nie zostanie prawidłowo dodane. Konieczne będzie ręczna zmieniana sterownika urządzenia w „SmartThings Groovy IDE”. W tym celu wchodzimy na stronę – link. Logujemy się na swoje konto Samsung.

Kolejno wchodzimy w „My Device” i odnajdujemy termostat, może się kryć pod inną nazwą. Klikamy nazwę i przechodzimy do szczegółów urządzenia, na końcu strony, klikamy „Edit”.

Tutaj musimy zmienić typ (moduł sterowania) urządzenia, klikamy „Type” i rozwijamy listę sterowników. Na dole rozwiniętej listy powinien znajdować się nasz niestandardowy program obsługi – „Aeotec aerQ Sensor”. Po tym klikamy „Update”, po chwili urządzenie powinno zacząć prawidłowo funkcjonować z aplikacją mobilną SmartThings.

Szczegółowe informacje dotyczące konfiguracji, można uzyskać również na oficjalnej stronie wsparcia – tutaj.

Reset czujnika

Procedura resetu pozwala na przywrócenie urządzenia do ustawień fabrycznych, co skutkuje usunięciem wszystkich informacji o przypisaniu do sieci oraz ustawień konfiguracyjnych. Aby zresetować urządzenie do ustawień fabrycznych: należy nacisnąć i przytrzymać przycisk na sensorze przez 20 sekund. Procedury należy używać tylko wtedy, gdy główny kontroler nie działa lub istnieje inny problem.

Gdzie zakupić ?

Aeotec aërQ Temperature & Humidity Sensor jest dostępny w sklepie Z-Home, w cenie 189 zł. W pudełku znajdziemy czujnik, baterię, dwustronny przylepiec i instrukcję obsługi w języku angielskim. Podręcznik użytkowania można znaleźć tutaj.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google

Komentujesz korzystając z konta Google. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s