Testing Netatmo Weather station and using PHP API

Robert AndresenInternet of things, Programming Leave a Comment

We just bought some Netatmo devices (Weather station components) at work, to monitor the air-quality in our office. After using it for 3 days, I am a bit stunned over how much you can use this devices for.

The data itself are interesting enough, but when integrating this data in to a smarthome, you can actually «see» where the people are.

Netatmo also provides a great API, to integrate the devices with other systems.

The chart data

After looking at the chart in the Netatmo webapplication, I am actually stunned of how much this data can be used for. If I buy this at home, I can not only monitor the air-quality, but also track if there are people at home and build events based on this data.

netatmo_co2_chart

The negative sides

When you buy the «start-kit» it comes with an indoor and outdoor module. The indoor module can measure pressure and sound (dB). In addition to these two devices, you can have max three other add-on devices. We bought three indoor add-on devices, so the only way to add more devices, is to buy additional «start-kit» *.

Another negative thing is that this add-on modules does not have pressure and sound (dB). The pressure would most likely be the same, but the dB would be interesting to have on all devices.

I also wondered of how the CO2 is measured and how it calibrates. They are actually calibrating themselves, by taking the lowest value over one week period. They recommend airing the rooms fully once a week, to get a base value for 400 ppm. Looks like they assumes the outdoor value would be 400ppm. This means that you can never know how correct the CO2 value actually is (without having a very expensive calibrated device to compare with).

* I have to say that it looks like they have created a good way to link different locations and users together. If I buy Netatmo and install the devices at my own home, I can add myself as and administrator for the account at work as well. This means that the max device limit is not a problem if you buy multiple kits with main devices.

Shared with the world

In the webapplication you can share your data on social media (like twitter) – but I also found that the weather data is publicly available on the Netatmo weather map: https://www.netatmo.com/weathermap.

I’m not sure how I feel about this… It’s a really great feature, as you get real time weather-data from all over the world. Most weather services has an algorithm that calculates the weather-data in different places from the nearby sensors. Netatmo-data will probably give you more accurate data.

The downside is that your personal weather data is automatically shared with the world. I haven’t read the license agreement yet, but in theory Netatmo can sell your «private» data to a third-party.

How reliable this public data is, is another question, as some users can have the outdoor-module inside and others could have placed it on the sunny side of the house.

The API

Start here: https://dev.netatmo.com/
See also the PHP SDK they have on GitHub: https://github.com/Netatmo/Netatmo-API-PHP

I wrote a sample code I wrote for my own system, to log devices into my smarthome database. I used client authorization instead of token, so I have to store the Netatmo username and password in my database.

The code requires NAApiClient.php (dead link) and Config.php (dead link) from they’re PHP SDK on GitHub. My code builds an array with all the devices and values. The code uses my own classes for my MySmartHome, so it does not work «out-of-the-box» for you – but use it as an example 🙂

<?php
	require_once( dirname(__FILE__) . '/config.php' ); // Connect to database and include netatmo classes

	// Get and loop all users
	$getUsers = $objUsers->getUsers();
	foreach ($getUsers as $userID => $u) {

	
		// Get netatmo userdata
		$userData = $obj->getUserData($userID);
		
		// Run if user has settings
		if ($userData) {


			// Auth
			$scope = NAScopes::SCOPE_READ_STATION;
			$client = new NAApiClient(array("client_id" => $userData['client_id'], "client_secret" => $userData['client_secret'], "username" => $userData['username'], "password" => $userData['password'], "scope" => $scope));
			$helper = new NAApiHelper($client);
			try {
			    $tokens = $client->getAccessToken();
			} catch(NAClientException $ex) {
			    echo "An error happend while trying to retrieve your tokens\n";
			    exit(-1);
			}


			// Request devices
			$user = $helper->api("getuser", "POST"); // Retrieve User Info 
			$devicelist = $helper->simplifyDeviceList();

			/*
				Loop devices
				As main unit has sub-units, we build a new array to process
			*/
			foreach ($devicelist['devices'] as $key => $dData) {
				$deviceID = trim($dData['_id']);

				$d[$deviceID]['extID'] = $deviceID;
				$d[$deviceID]['name'] = trim($dData['module_name']);
				$d[$deviceID]['latitude'] = trim($dData['place']['location'][0]);
				$d[$deviceID]['longitude'] = trim($dData['place']['location'][1]);

				$d[$deviceID]['values']['time_read'] = trim($dData['dashboard_data']['time_utc']);
				$d[$deviceID]['values']['temperature'] = trim($dData['dashboard_data']['Temperature']);
				$d[$deviceID]['values']['noise'] = trim($dData['dashboard_data']['Noise']);
				$d[$deviceID]['values']['humidity'] = trim($dData['dashboard_data']['Humidity']);
				$d[$deviceID]['values']['pressure'] = trim($dData['dashboard_data']['Pressure']);
				$d[$deviceID]['values']['CO2'] = trim($dData['dashboard_data']['CO2']);

				// Loop submodules of main unit
				foreach ($dData['modules'] as $key2 => $dModules) {
					$moduleID = trim($dModules['_id']);

					$d[$moduleID]['extID'] = $moduleID;
					$d[$moduleID]['name'] = trim($dModules['module_name']);
					$d[$moduleID]['battery'] = trim($dModules['battery_vp']);

					$d[$moduleID]['values']['time_read'] = trim($dModules['dashboard_data']['time_utc']);
					
					if (isset($dModules['dashboard_data']['temperature']))
						$d[$moduleID]['values']['temperature'] = trim($dModules['dashboard_data']['Temperature']);
					
					if (isset($dModules['dashboard_data']['humidity']))
						$d[$moduleID]['values']['humidity'] = trim($dModules['dashboard_data']['Humidity']);
					
					if (isset($dModules['dashboard_data']['CO2']))
						$d[$moduleID]['values']['CO2'] = trim($dModules['dashboard_data']['CO2']);
				}
			}
			


			/*
				Loop builded array with devices
				1. Insert/Update devices
				2. Add sensor-data to log
			*/

			foreach ($d as $extID => $nDevice) {
				
				// Check if device exist
				$query = "SELECT * FROM msh_devices WHERE binding LIKE 'netatmo' AND user_id='{$thisUser['user_id']}' AND device_ext_id LIKE '$extID'";
				$result = $mysqli->query($query);
				$numRows = $result->num_rows;

				if ($numRows == 0) // Not exist, create new device
				{
					$p = array (
						'user_id' => $thisUser['user_id'],
						'binding' => 'netatmo',
						'device_ext_id' => $extID,
						'device_name' => $nDevice['name'],
						'latitude' => $nDevice['latitude'],
						'longitude' => $nDevice['longitude'],
						'battery' => $nDevice['battery'],
						'monitor' => 1,
					);
					$device_int_id = $objDevices->addDevice($p);
				}

				else // Device exist, update parameters 
				{
					$thisDevice = $result->fetch_array(); //Get existing device data
					$device_int_id = $thisDevice['device_int_id']; // Get current Int ID

					$p = array (
						'device_int_id' => $device_int_id,
						'binding' => 'netatmo',
						'device_ext_id' => $extID,
						'device_name' => $nDevice['name'],
						'latitude' => $nDevice['latitude'],
						'longitude' => $nDevice['longitude'],
						'battery' => $nDevice['battery'],
					);
					$result = $objDevices->updateDevice($p);
				}



				// Add log
				if ($thisDevice['monitor'] == 1) // Only log if device should be monitored
				{
					/*
						Loop all sensor data
						Array like
							temperature => value,
							noise => value
							...
					*/
					foreach ($nDevice['values'] as $key => $vData) {
						$error = false;

						if (empty($device_int_id)) {
							$error = true;
						}

						if (empty($nDevice['values']['time_read'])) {
							$error = true;
						}


						// Check for different keys (sensor-types), set correct units for them and set sensor value
						// The different units are stored in DB Table:msh_units
						if ($key == 'temperature') {
							$unit_id = 1;
							$sensValue = $nDevice['values']['temperature'];
						}
						elseif ($key == 'noise') {
							$unit_id = 8;
							$sensValue = $nDevice['values']['noise'];
						}
						elseif ($key == 'humidity') {
							$unit_id = 2;
							$sensValue = $nDevice['values']['humidity'];
						}
						elseif ($key == 'pressure') {
							$unit_id = 10;
							$sensValue = $nDevice['values']['pressure'];
						}
						elseif ($key == 'CO2') {
							$unit_id = 9;
							$sensValue = $nDevice['values']['CO2'];
						}
						else {
							$error = true;
						}

						// Replace any , with . for DB
						$sensValue = str_replace(',', '.', $sensValue);

						// Add sensordata to log
						$p = array (
							'device_int_id' => $device_int_id,
							'time' => $nDevice['values']['time_read'],
							'unit_id' => $unit_id,
							'value' => $sensValue,
						);

						if (!$error) {
							$addLogResult = $objDevices->addLog($p);
						} else {
							// ...					
						}

					} //end-foreach value data
				} //end-if monitor
				//echo "<hr />";
			} //end-foreach devices
		} //end-if-userData
	} //end-getUsers()
?>