-
Notifications
You must be signed in to change notification settings - Fork 35
/
Home_Assistant.ino
218 lines (182 loc) · 8.48 KB
/
Home_Assistant.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
Home_Assistant.ino
Example code for sending environment data from the Metriful MS430 to
an installation of Home Assistant on your local WiFi network.
For more information, visit www.home-assistant.io
This example is designed for the following WiFi enabled hosts:
* Arduino Nano 33 IoT
* Arduino MKR WiFi 1010
* ESP8266 boards (e.g. Wemos D1, NodeMCU)
* ESP32 boards (e.g. DOIT DevKit v1)
Data are sent at regular intervals over your WiFi network to Home
Assistant and can be viewed on the dashboard or used to control
home automation tasks. More setup information is provided in the
Readme and User Guide.
Copyright 2020 Metriful Ltd.
Licensed under the MIT License - for further details see LICENSE.txt
For code examples, datasheet and user guide, visit
https://github.com/metriful/sensor
*/
#include <Metriful_sensor.h>
#include <WiFi_functions.h>
//////////////////////////////////////////////////////////
// USER-EDITABLE SETTINGS
// How often to read and report the data (every 3, 100 or 300 seconds)
uint8_t cycle_period = CYCLE_PERIOD_100_S;
// The details of the WiFi network:
char SSID[] = "PUT WIFI NETWORK NAME HERE IN QUOTES"; // network SSID (name)
char password[] = "PUT WIFI PASSWORD HERE IN QUOTES"; // network password
// Home Assistant settings
// You must have already installed Home Assistant on a computer on your
// network. Go to www.home-assistant.io for help on this.
// Choose a unique name for this MS430 sensor board so you can identify it.
// Variables in HA will have names like: SENSOR_NAME.temperature, etc.
#define SENSOR_NAME "kitchen3"
// Change this to the IP address of the computer running Home Assistant.
// You can find this from the admin interface of your router.
#define HOME_ASSISTANT_IP "192.168.43.144"
// Security access token: the Readme and User Guide explain how to get this
#define LONG_LIVED_ACCESS_TOKEN "PASTE YOUR TOKEN HERE WITHIN QUOTES"
// END OF USER-EDITABLE SETTINGS
//////////////////////////////////////////////////////////
#if !defined(HAS_WIFI)
#error ("This example program has been created for specific WiFi enabled hosts only.")
#endif
WiFiClient client;
// Buffers for assembling http POST requests
char postBuffer[450] = {0};
char fieldBuffer[70] = {0};
// Structs for data
AirData_t airData = {0};
AirQualityData_t airQualityData = {0};
LightData_t lightData = {0};
ParticleData_t particleData = {0};
SoundData_t soundData = {0};
// Define the display attributes of data sent to Home Assistant.
// The chosen name, unit and icon will appear in on the overview
// dashboard in Home Assistant. The icons can be chosen from
// https://cdn.materialdesignicons.com/5.3.45/
// (remove the "mdi-" part from the icon name).
// The attribute fields are: {name, unit, icon, decimal places}
HA_Attributes_t pressure = {"Pressure","Pa","weather-cloudy",0};
HA_Attributes_t humidity = {"Humidity","%","water-percent",1};
HA_Attributes_t illuminance = {"Illuminance","lx","white-balance-sunny",2};
HA_Attributes_t soundLevel = {"Sound level","dBA","microphone",1};
HA_Attributes_t peakAmplitude = {"Sound peak","mPa","waveform",2};
HA_Attributes_t AQI = {"Air Quality Index"," ","thought-bubble-outline",1};
HA_Attributes_t AQ_assessment = {"Air quality assessment","","flower-tulip",0};
#if (PARTICLE_SENSOR == PARTICLE_SENSOR_PPD42)
HA_Attributes_t particulates = {"Particle concentration","ppL","chart-bubble",0};
#else
HA_Attributes_t particulates = {"Particle concentration",SDS011_UNIT_SYMBOL,"chart-bubble",2};
#endif
#ifdef USE_FAHRENHEIT
HA_Attributes_t temperature = {"Temperature",FAHRENHEIT_SYMBOL,"thermometer",1};
#else
HA_Attributes_t temperature = {"Temperature",CELSIUS_SYMBOL,"thermometer",1};
#endif
void setup() {
// Initialize the host's pins, set up the serial port and reset:
SensorHardwareSetup(I2C_ADDRESS);
connectToWiFi(SSID, password);
// Apply settings to the MS430 and enter cycle mode
uint8_t particleSensorCode = PARTICLE_SENSOR;
TransmitI2C(I2C_ADDRESS, PARTICLE_SENSOR_SELECT_REG, &particleSensorCode, 1);
TransmitI2C(I2C_ADDRESS, CYCLE_TIME_PERIOD_REG, &cycle_period, 1);
ready_assertion_event = false;
TransmitI2C(I2C_ADDRESS, CYCLE_MODE_CMD, 0, 0);
}
void loop() {
// Wait for the next new data release, indicated by a falling edge on READY
while (!ready_assertion_event) {
yield();
}
ready_assertion_event = false;
// Read data from the MS430 into the data structs.
ReceiveI2C(I2C_ADDRESS, AIR_DATA_READ, (uint8_t *) &airData, AIR_DATA_BYTES);
ReceiveI2C(I2C_ADDRESS, AIR_QUALITY_DATA_READ, (uint8_t *) &airQualityData, AIR_QUALITY_DATA_BYTES);
ReceiveI2C(I2C_ADDRESS, LIGHT_DATA_READ, (uint8_t *) &lightData, LIGHT_DATA_BYTES);
ReceiveI2C(I2C_ADDRESS, SOUND_DATA_READ, (uint8_t *) &soundData, SOUND_DATA_BYTES);
ReceiveI2C(I2C_ADDRESS, PARTICLE_DATA_READ, (uint8_t *) &particleData, PARTICLE_DATA_BYTES);
// Check that WiFi is still connected
uint8_t wifiStatus = WiFi.status();
if (wifiStatus != WL_CONNECTED) {
// There is a problem with the WiFi connection: attempt to reconnect.
Serial.print("Wifi status: ");
Serial.println(interpret_WiFi_status(wifiStatus));
connectToWiFi(SSID, password);
ready_assertion_event = false;
}
uint8_t T_intPart = 0;
uint8_t T_fractionalPart = 0;
bool isPositive = true;
getTemperature(&airData, &T_intPart, &T_fractionalPart, &isPositive);
// Send data to Home Assistant
sendNumericData(&temperature, (uint32_t) T_intPart, T_fractionalPart, isPositive);
sendNumericData(&pressure, (uint32_t) airData.P_Pa, 0, true);
sendNumericData(&humidity, (uint32_t) airData.H_pc_int, airData.H_pc_fr_1dp, true);
sendNumericData(&illuminance, (uint32_t) lightData.illum_lux_int, lightData.illum_lux_fr_2dp, true);
sendNumericData(&soundLevel, (uint32_t) soundData.SPL_dBA_int, soundData.SPL_dBA_fr_1dp, true);
sendNumericData(&peakAmplitude, (uint32_t) soundData.peak_amp_mPa_int,
soundData.peak_amp_mPa_fr_2dp, true);
sendNumericData(&AQI, (uint32_t) airQualityData.AQI_int, airQualityData.AQI_fr_1dp, true);
if (PARTICLE_SENSOR != PARTICLE_SENSOR_OFF) {
sendNumericData(&particulates, (uint32_t) particleData.concentration_int,
particleData.concentration_fr_2dp, true);
}
sendTextData(&AQ_assessment, interpret_AQI_value(airQualityData.AQI_int));
}
// Send numeric data with specified sign, integer and fractional parts
void sendNumericData(const HA_Attributes_t * attributes, uint32_t valueInteger,
uint8_t valueDecimal, bool isPositive) {
char valueText[20] = {0};
const char * sign = isPositive ? "" : "-";
switch (attributes->decimalPlaces) {
case 0:
default:
sprintf(valueText,"%s%" PRIu32, sign, valueInteger);
break;
case 1:
sprintf(valueText,"%s%" PRIu32 ".%u", sign, valueInteger, valueDecimal);
break;
case 2:
sprintf(valueText,"%s%" PRIu32 ".%02u", sign, valueInteger, valueDecimal);
break;
}
http_POST_Home_Assistant(attributes, valueText);
}
// Send a text string: must have quotation marks added
void sendTextData(const HA_Attributes_t * attributes, const char * valueText) {
char quotedText[20] = {0};
sprintf(quotedText,"\"%s\"", valueText);
http_POST_Home_Assistant(attributes, quotedText);
}
// Send the data to Home Assistant as an HTTP POST request.
void http_POST_Home_Assistant(const HA_Attributes_t * attributes, const char * valueText) {
client.stop();
if (client.connect(HOME_ASSISTANT_IP, 8123)) {
// Form the URL from the name but replace spaces with underscores
strcpy(fieldBuffer,attributes->name);
for (uint8_t i=0; i<strlen(fieldBuffer); i++) {
if (fieldBuffer[i] == ' ') {
fieldBuffer[i] = '_';
}
}
sprintf(postBuffer,"POST /api/states/" SENSOR_NAME ".%s HTTP/1.1", fieldBuffer);
client.println(postBuffer);
client.println("Host: " HOME_ASSISTANT_IP ":8123");
client.println("Content-Type: application/json");
client.println("Authorization: Bearer " LONG_LIVED_ACCESS_TOKEN);
// Assemble the JSON content string:
sprintf(postBuffer,"{\"state\":%s,\"attributes\":{\"unit_of_measurement\""
":\"%s\",\"friendly_name\":\"%s\",\"icon\":\"mdi:%s\"}}",
valueText, attributes->unit, attributes->name, attributes->icon);
sprintf(fieldBuffer,"Content-Length: %u", strlen(postBuffer));
client.println(fieldBuffer);
client.println();
client.print(postBuffer);
}
else {
Serial.println("Client connection failed.");
}
}