Skip to content

Commit

Permalink
Merge pull request #38 from leoherzog/ecowitt
Browse files Browse the repository at this point in the history
Add Ecowitt Data Source
  • Loading branch information
leoherzog authored Dec 15, 2024
2 parents 4c97a6b + 86e2f7e commit 8bfad85
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 31 deletions.
67 changes: 39 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This code is built to be hosted on the free [Google Apps Script](https://develop
- [Davis WeatherLink](https://weatherlink.com/),
- [WeatherFlow Tempest](https://tempestwx.com/),
- [Ambient Weather](https://ambientweather.net/),
- [Ecowitt](https://ecowitt.net/),
- [aprs.fi (CWOP)](https://aprs.fi/), or
- a custom data source in [RTL_433 JSON format](https://www.triq.org/rtl_433/DATA_FORMAT.html),

Expand Down Expand Up @@ -79,22 +80,32 @@ and periodically sends it on to
- Set your `ambientWeatherStationName` on line 27
- Set your `ambientWeatherApiKey` on Line 28
</details>
<details>
<summary>Ecowitt</summary>

Uses the [Ecowitt.net](https://www.ecowitt.net/) API.

- Set the `datasource` to `ecowitt` on Line 10
- Set your `ecowittAPIKey` from your [Ecowitt.net Private Center](https://www.ecowitt.net/home/user) on Line 30
- Set your `ecowittApplicationKey` from your [Ecowitt.net Private Center](https://www.ecowitt.net/home/user) on Line 32
- Set your `ecowittMacAddress` (find in your [Device List](https://www.ecowitt.net/home/manage)) on Line 32
</details>
<details>
<summary>aprs.fi (CWOP)</summary>

Uses the [aprs.fi API](https://aprs.fi/page/api) to fetch APRS packet data from a CWOP station.

- Set the `datasource` to `aprs` on Line 10
- Set your `aprsStationID` on line 30
- Set your `aprsApiKey` on Line 31 from [your aprs.fi account](https://aprs.fi/account/)
- Set your `aprsStationID` on line 34
- Set your `aprsApiKey` on Line 35 from [your aprs.fi account](https://aprs.fi/account/)
</details>
<details>
<summary>Custom Data Source</summary>

Send weather station readings from any system in [RTL_433 JSON format](https://www.triq.org/rtl_433/DATA_FORMAT.html).

- Set the `datasource` to `custom` on Line 10
- Set your station's latitude and longitude on lines 33 and 34 in decimal degrees
- Set your station's latitude and longitude on lines 37 and 38 in decimal degrees
- Click `Deploy ▼` → New deployment → '⚙' → Web app, and change 'Who has access' to 'Anyone' and press 'Deploy'
- Begin HTTP POSTing JSON data to the `https://script.google.com/macros/...` URL provided in the confirmation dialog
</details>
Expand All @@ -106,27 +117,27 @@ and periodically sends it on to

To send to [Wunderground](https://support.weather.com/s/article/PWS-Upload-Protocol):

- Set `updateWunderground` to `true` on Line 38
- Set your `wundergroundAPIKey` on Line 39
- Set your `wundergroundStationID` on line 40
- Set `updateWunderground` to `true` on Line 42
- Set your `wundergroundAPIKey` on Line 43
- Set your `wundergroundStationID` on line 44
</details>
<details>
<summary>Windy.com</summary>

To send to [Windy.com](https://community.windy.com/topic/8168/report-your-weather-station-data-to-windy):

- Set `updateWindy` to `true` on Line 42
- Set your `windyAPIKey` on Line 43
- Set your `windyStationID` on line 44. It's likely `0`, `1`, `2`, etc.
- Set `updateWindy` to `true` on Line 46
- Set your `windyAPIKey` on Line 47
- Set your `windyStationID` on line 48. It's likely `0`, `1`, `2`, etc.
</details>
<details>
<summary>Aeris PWSWeather</summary>

To send to [PWSWeather](https://dashboard.pwsweather.com/):

- Set `updatePWSWeather` to `true` on Line 46
- Set your `pwsWeatherAPIKey` from your station's profile page on line 47
- Set your `pwsWeatherStationID` on Line 48
- Set `updatePWSWeather` to `true` on Line 50
- Set your `pwsWeatherAPIKey` from your station's profile page on line 51
- Set your `pwsWeatherStationID` on Line 52
</details>
<details>
<summary>WeatherCloud</summary>
Expand All @@ -135,19 +146,19 @@ and periodically sends it on to

Retrieve your station's ID and API Key by going to [your Devices](https://app.weathercloud.net/devices), then clicking Settings → 🔌 Link on your station.

- Set `updateWeatherCloud` to `true` on Line 50
- Set your `weathercloudAPIKey` on Line 51
- Set your `weathercloudID` on Line 52
- Set whether or not you have a WeatherCloud Pro or Premium account with `hasWeatherCloudPro` as `true` or `false` on line 53
- Set `updateWeatherCloud` to `true` on Line 54
- Set your `weathercloudAPIKey` on Line 55
- Set your `weathercloudID` on Line 56
- Set whether or not you have a WeatherCloud Pro or Premium account with `hasWeatherCloudPro` as `true` or `false` on line 57
</details>
<details>
<summary>OpenWeatherMap</summary>

Creation of a new OpenWeatherMap station must be done by API, not on the OpenWeatherMap website. More information is available in [the OpenWeatherMap Station API documentation](https://openweathermap.org/stations#create_station). The basic concept for what must be done is available in the `createNewOWMStation_()` function. Remove the `_` character from the name of that function to make it selectable from the `▷ Run` button in the toolbar. If you do so, make sure you note your new station's ID and other details in the log (available in the Executions tab in the sidebar after running!), then:

- Set `updateOpenWeatherMap` to `true` on Line 55
- Set `openWeatherMapAPIKey` to your [API Key](https://home.openweathermap.org/api_keys) on Line 56
- Set your `openWeatherMapStationId` to [your OpenWeatherMap station's `external_id`](https://openweathermap.org/stations#create_station) on line 57
- Set `updateOpenWeatherMap` to `true` on Line 59
- Set `openWeatherMapAPIKey` to your [API Key](https://home.openweathermap.org/api_keys) on Line 60
- Set your `openWeatherMapStationId` to [your OpenWeatherMap station's `external_id`](https://openweathermap.org/stations#create_station) on line 61
</details>
<details>
<summary>WindGuru</summary>
Expand All @@ -156,9 +167,9 @@ and periodically sends it on to

Start by [registering a new "Other / Upload API" station](https://stations.windguru.cz/register.php?id_type=16), then:

- Set `updateWindGuru` to `true` on Line 59
- Set `windGuruStationUID` to your chosen [station UID](https://stations.windguru.cz/) on Line 60
- Set your `windGuruStationPassword` to your chosen [station API password](https://stations.windguru.cz/) (note, not your _account's_ password) on line 61
- Set `updateWindGuru` to `true` on Line 63
- Set `windGuruStationUID` to your chosen [station UID](https://stations.windguru.cz/) on Line 64
- Set your `windGuruStationPassword` to your chosen [station API password](https://stations.windguru.cz/) (note, not your _account's_ password) on line 65
</details>
<details>
<summary>Met (UK) WOW</summary>
Expand All @@ -167,18 +178,18 @@ and periodically sends it on to

Start by [registering a new Site](https://wow.metoffice.gov.uk/sites/create). During registration, one of the fields in your Site's options is "Authentication Key". Choose any 6-Digit number. Then:

- Set `updateWOW` to `true` on Line 63
- Set `wowSiteID` to the generated [Site ID](https://wow.metoffice.gov.uk/sites) on Line 64
- Set `wowAuthKey` to your chosen [6-Digit Authentication Key](https://wow.metoffice.gov.uk/sites) that you chose when creating or editing the Site on line 65
- Set `updateWOW` to `true` on Line 67
- Set `wowSiteID` to the generated [Site ID](https://wow.metoffice.gov.uk/sites) on Line 68
- Set `wowAuthKey` to your chosen [6-Digit Authentication Key](https://wow.metoffice.gov.uk/sites) that you chose when creating or editing the Site on line 69
</details>
<details>
<summary>NOAA Citizen Weather Observer Program (CWOP)</summary>

Send to [CWOP](https://madis.ncep.noaa.gov/madis_cwop.shtml). Start by [registering for a new station](https://madis.ncep.noaa.gov/madis_cwop.shtml), then when you receive your email:

- Set `updateCWOP` to `true` on Line 67
- Set `cwopStationIDOrHamCallsign` to your assigned CWOP station ID that you received via email on Line 68
- If you are using your ham radio callsign as your station ID and you have received a validation code from NOAA CWOP support, set `cwopValidationCode` to your validation code on Line 69
- Set `updateCWOP` to `true` on Line 71
- Set `cwopStationIDOrHamCallsign` to your assigned CWOP station ID that you received via email on Line 72
- If you are using your ham radio callsign as your station ID and you have received a validation code from NOAA CWOP support, set `cwopValidationCode` to your validation code on Line 73
</details>

4. Run the "Schedule" function (not the "doPost" function) by selecting "Schedule" in the dropdown and pressing the `▷ Run` button in the toolbar. You're done! You can see it periodically running in the `☰▶` Executions tab on the left sidebar. This code is executed on Google's servers and does not require a computer to remain on.
Expand Down
120 changes: 117 additions & 3 deletions code.gs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

// Getting data

const datasource = 'weatherflow'; // 'ibm' (wunderground), 'acurite' (myacurite), 'davis' (weatherlink), 'weatherflow' (tempestwx), 'ambient' (ambient weather), 'aprs' (aprs.fi), or 'custom' (custom webhook in rtl_433 format)
const datasource = 'weatherflow'; // 'ibm' (wunderground), 'acurite' (myacurite), 'davis' (weatherlink), 'weatherflow' (tempestwx), 'ambient' (ambient weather), 'ecowitt', 'aprs' (aprs.fi), or 'custom' (custom webhook in rtl_433 format)

const ibmAPIKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const ibmStationID = 'KXXXXXXXXXX';
Expand All @@ -27,6 +27,10 @@ const weatherflowStationId = 'xxxxx';
const ambientWeatherStationName = 'xxxxxx';
const ambientWeatherApiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// or
const ecowittAPIKey = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
const ecowittApplicationKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const ecowittMacAddress = 'XX:XX:XX:XX:XX:XX';
// or
const aprsStationID = 'xxxxxx';
const aprsApiKey = 'xxxxxx.xxxxxxxxxxxxxxxx';
// or
Expand Down Expand Up @@ -79,7 +83,7 @@ const cwopValidationCode = null;
*/

let version = 'v2.8.2';
let version = 'v2.9.0';

function Schedule() {
if (updateWunderground && datasource === 'ibm' && ibmStationID === wundergroundStationID) throw 'Error: You are currently set to pull data from Wunderground and also send data to Wunderground. Please disable one or the other to avoid duplicate data.';
Expand All @@ -106,6 +110,10 @@ function Schedule() {
refreshFromAmbientWeather_();
ScriptApp.newTrigger('refreshFromAmbientWeather_').timeBased().everyMinutes(1).create();
break;
case 'ecowitt':
refreshFromEcowitt_();
ScriptApp.newTrigger('refreshFromEcowitt_').timeBased().everyMinutes(1).create();
break;
case 'aprs':
refreshFromAPRSFI_();
ScriptApp.newTrigger('refreshFromAPRSFI_').timeBased().everyMinutes(1).create();
Expand Down Expand Up @@ -631,8 +639,114 @@ function refreshFromAmbientWeather_() {

}

// doc.ecowitt.net
function refreshFromEcowitt_() {

let ecowittConditions = fetchJSON_('https://api.ecowitt.net/api/v3/device/real_time?application_key=' + ecowittApplicationKey + '&api_key=' + ecowittAPIKey + '&mac=' + ecowittMacAddress + '&call_back=all&temp_unitid=2&pressure_unitid=4&wind_speed_unitid=9&rainfall_unitid=13&solar_irradiance_unitid=16');
if (!ecowittConditions || ecowittConditions.code !== 0) return false; // still no luck? give up
// console.log(JSON.stringify(ecowittConditions));

let conditions = {};
conditions.time = new Date(Number(ecowittConditions.time) * 1000).getTime();
if (ecowittConditions.data?.outdoor) {
let outdoor = ecowittConditions.data.outdoor;
if (outdoor.temperature?.value) {
conditions.temp = {
"f": Number(outdoor.temperature.value).toFixedNumber(2),
"c": Number(outdoor.temperature.value).fToC().toFixedNumber(2)
};
}
if (outdoor.dew_point?.value) {
conditions.dewpoint = {
"f": Number(outdoor.dew_point.value).toFixedNumber(2),
"c": Number(outdoor.dew_point.value).fToC().toFixedNumber(2)
};
}
if (outdoor.humidity?.value) {
conditions.humidity = Number(outdoor.humidity.value).toFixedNumber(0);
}
}
if (ecowittConditions.data?.wind) {
let wind = ecowittConditions.data.wind;
if (wind.wind_speed?.value) {
conditions.windSpeed = {
"mph": Number(wind.wind_speed.value).toFixedNumber(2),
"mps": Number(wind.wind_speed.value).mphToMPS().toFixedNumber(2),
"kph": Number(wind.wind_speed.value).mphToKPH().toFixedNumber(2),
"knots": Number(wind.wind_speed.value).mphToKnots().toFixedNumber(2)
};
}
if (wind.wind_gust?.value) {
conditions.windGust = {
"mph": Number(wind.wind_gust.value).toFixedNumber(2),
"mps": Number(wind.wind_gust.value).mphToMPS().toFixedNumber(2),
"kph": Number(wind.wind_gust.value).mphToKPH().toFixedNumber(2),
"knots": Number(wind.wind_gust.value).mphToKnots().toFixedNumber(2)
};
}
if (wind.wind_direction?.value) {
conditions.winddir = Number(wind.wind_direction.value);
}
}
if (ecowittConditions.data?.pressure?.relative?.value) {
conditions.pressure = {
"inHg": Number(ecowittConditions.data.pressure.relative.value).toFixedNumber(3),
"hPa": Number(ecowittConditions.data.pressure.relative.value).inHgTohPa().toFixedNumber(0)
};
}
if (ecowittConditions.data?.solar_and_uvi) {
let solar = ecowittConditions.data.solar_and_uvi;
if (solar.solar?.value) {
conditions.solarRadiation = Number(solar.solar.value);
}
if (solar.uvi?.value) {
conditions.uv = Number(solar.uvi.value);
}
}
if (ecowittConditions.data?.rainfall) {
let rain = ecowittConditions.data.rainfall;
if (rain.rain_rate?.value) {
conditions.precipRate = {
"in": Number(rain.rain_rate.value).toFixedNumber(3),
"mm": Number(rain.rain_rate.value).inTomm().toFixedNumber(2)
};
}
if (rain.daily?.value) {
conditions.precipSinceMidnight = {
"in": Number(rain.daily.value).toFixedNumber(3),
"mm": Number(rain.daily.value).inTomm().toFixedNumber(2)
};
}
if (rain.hourly?.value) {
conditions.precipLastHour = {
"in": Number(rain.hourly.value).toFixedNumber(3),
"mm": Number(rain.hourly.value).inTomm().toFixedNumber(2)
};
}
}
if (conditions.temp && conditions.windSpeed) {
conditions.windChill = {
"f": conditions.temp.f.windChillF(conditions.windSpeed.mph).toFixedNumber(2),
"c": conditions.temp.c.windChillC(conditions.windSpeed.kph).toFixedNumber(2)
};
}
if (conditions.temp && conditions.humidity) {
conditions.heatIndex = {
"f": conditions.temp.f.heatIndex(conditions.humidity, 'F').toFixedNumber(2),
"c": conditions.temp.c.heatIndex(conditions.humidity, 'C').toFixedNumber(2)
};
}

console.log(JSON.stringify(conditions));

CacheService.getScriptCache().put('conditions', JSON.stringify(conditions), 21600);

return JSON.stringify(conditions);

}

// https://aprs.fi/page/api
function refreshFromAPRSFI() {
function refreshFromAPRSFI_() {

let aprsStations = fetchJSON_('https://api.aprs.fi/api/get?name=' + aprsStationID + '&what=wx&format=json&apikey=' + aprsApiKey);
if (!aprsStations || !aprsStations?.entries || !aprsStations?.entries.length) return false; // still no luck? give up
Expand Down

0 comments on commit 8bfad85

Please sign in to comment.