Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
ecowitt2mqtt
is a small CLI/web server that can receive data from Fine Offset weather
stations (and their numerous white-labeled counterparts, like Ecowitt and
Ambient Weather), adjust that data in numerous ways, and send it on to one or more
MQTT brokers.
pip install ecowitt2mqtt
ecowitt2mqtt
is currently supported on:
The datapoints within this library and documentation constitute estimates and are intended to help informed decision making. They should not replace analysis, advice, or diagnosis from trained professionals. Use this data at your own discretion.
Despite the name of the library, ecowitt2mqtt
should support any weather station/gateway
that is produced by Fine Offset. This includes brands that
white-label Fine Offset equipment, such as:
...and many others. For more information on how these brands relate to one another, see this forum post: https://www.wxforum.net/index.php?topic=40730.0
Although there are some small differences between how these various branded devices are
configured, ecowitt2mqtt
endeavors to incorporate them all with minimal effort on the
user's part.
First, install ecowitt2mqtt
via pip
:
$ pip install ecowitt2mqtt
Next, if you haven't already, install the appropriate mobile app to configure your device. For example:
Find the appropriate location in the mobile app to configure a customized upload target for the station's data. This will differ depending on the app, but in general, you should select your device and find a screen entitled "Upload" (or similar).
Fill out the form with the appropriate values and tap Save
:
Protocol Type Same As
: pick the label that matches your brand (e.g., Ecowitt
for
Ecowitt devices)Server IP / Hostname
: the IP address/hostname of the device running ecowitt2mqtt
Path
: /data/report/
(the default path used by most mobile apps)Port
: 8080
(the default port on which ecowitt2mqtt
is served)Upload Interval
: 16 (a reasonable short number of seconds between publishes)Then, on the machine where you installed ecowitt2mqtt
, run it:
$ ecowitt2mqtt \
--mqtt-broker=192.168.1.101 \
--mqtt-username=user \
--mqtt-password=password \
--mqtt-topic=ecowitt2mqtt/device_1
--input-data-format=ecowitt
Within the Upload Interval
, data should begin to appear in the MQTT broker.
ecowitt2mqtt
can be configured via command line options, environment variables, or a
(YAML or JSON) config file.
usage: ecowitt2mqtt [-h] [--version] [--battery-override BATTERY_OVERRIDES] [--boolean-battery-true-value boolean_battery_true_value] [-c config] [--default-battery-strategy default_battery_strategy] [--diagnostics] [--disable-calculated-data] [-e endpoint] [--hass-discovery]
[--hass-discovery-prefix hass_discovery_prefix] [--hass-entity-id-prefix hass_entity_id_prefix] [--input-data-format input_data_format] [--input-unit-system input_unit_system] [-b mqtt_broker] [-p mqtt_password] [--mqtt-port mqtt_port] [--mqtt-retain] [--mqtt-tls] [-t mqtt_topic]
[-u mqtt_username] [--output-unit-system output_unit_system] [--output-unit-accumulated-precipitation output_unit_accumulated_precipitation] [--output-unit-distance output_unit_distance] [--output-unit-humidity output_unit_humidity]
[--output-unit-illuminance output_unit_illuminance] [--output-unit-precipitation-rate output_unit_precipitation_rate] [--output-unit-pressure output_unit_pressure] [--output-unit-speed output_unit_speed] [--output-unit-temperature output_unit_temperature] [--port port]
[--precision precision] [--raw-data] [-v]
Send data from an Ecowitt gateway to an MQTT broker
options:
-h, --help show this help message and exit
--version show program's version number and exit
--battery-override BATTERY_OVERRIDES
A battery configuration override (format: key,value)
--boolean-battery-true-value boolean_battery_true_value
The value that boolean battery sensors use to represent a True value (default: 1)
-c config, --config config
A path to a YAML or JSON config file
--default-battery-strategy default_battery_strategy
The default battery config strategy to use (default: boolean)
--diagnostics Output diagnostics
--disable-calculated-data
Disable the output of calculated sensors
-e endpoint, --endpoint endpoint
The relative endpoint/path to serve ecowitt2mqtt on (default: /data/report)
--hass-discovery Publish data in the Home Assistant MQTT Discovery format
--hass-discovery-prefix hass_discovery_prefix
The Home Assistant MQTT Discovery topic prefix to use (default: homeassistant)
--hass-entity-id-prefix hass_entity_id_prefix
The prefix to use for Home Assistant entity IDs. Example: A prefix of 'prefix' will prepend 'prefix_' to entity IDs
--input-data-format input_data_format
The input data format used by the gateway (default: ecowitt)
--input-unit-system input_unit_system
The input unit system used by the gateway (default: imperial)
--locale locale The locale to use (default: en_US.UTF-8)
-b mqtt_broker, --mqtt-broker mqtt_broker
The hostname or IP address of an MQTT broker
-p mqtt_password, --mqtt-password mqtt_password
A valid password for the MQTT broker
--mqtt-port mqtt_port
The listening port of the MQTT broker (default: 1883)
--mqtt-retain Instruct the MQTT broker to retain messages
--mqtt-tls Enable MQTT over TLS
-t mqtt_topic, --mqtt-topic mqtt_topic
The MQTT topic to publish device data to
-u mqtt_username, --mqtt-username mqtt_username
A valid username for the MQTT broker
--output-unit-system output_unit_system
The output unit system used by the gateway (default: imperial)
--output-unit-accumulated-precipitation output_unit_accumulated_precipitation
The output unit to use for accumulated precipitation data points (default: the default used by the output unit system)
--output-unit-distance output_unit_distance
The output unit to use for distance data points (default: the default used by the output unit system)
--output-unit-humidity output_unit_humidity
The output unit to use for humidity data points (default: the default used by the output unit system)
--output-unit-illuminance output_unit_illuminance
The output unit to use for illuminance data points (default: the default used by the output unit system)
--output-unit-precipitation-rate output_unit_precipitation_rate
The output unit to use for precipitation rate data points (default: the default used by the output unit system)
--output-unit-pressure output_unit_pressure
The output unit to use for pressure data points (default: the default used by the output unit system)
--output-unit-speed output_unit_speed
The output unit to use for speed data points (default: the default used by the output unit system)
--output-unit-temperature output_unit_temperature
The output unit to use for temperature data points (default: the default used by the output unit system)
--port port The port to serve ecowitt2mqtt on (default: 8080)
--precision precision
The precision to output data points at. Example: A value of 2 will round to two decimal places. (default: no limit)
--raw-data Return raw data (don't attempt to translate any values)
-v, --verbose Increase verbosity of logged output
ECOWITT2MQTT_BATTERY_OVERRIDE
: a semicolon-delimited list of key=value battery
overrides (default: numeric
)ECOWITT2MQTT_BOOLEAN_BATTERY_TRUE_VALUE
: The value that boolean battery sensors use to
represent a True value (default: 1
)ECOWITT2MQTT_CONFIG
: a path to a YAML or JSON config file (default: None
)ECOWITT2MQTT_DEFAULT_BATTERY_STRATEGY
: the default battery config strategy to use
(default: boolean
)ECOWITT2MQTT_DIAGNOSTICS
: whether to output diagnostics (default: false
)ECOWITT2MQTT_DISABLE_CALCULATED_DATA
: whether to disable the output of calculated
sensors (default: false
)ECOWITT2MQTT_ENDPOINT
: the relative endpoint/path to serve ecowitt2mqtt on (default:
/data/report
)ECOWITT2MQTT_HASS_DISCOVERY_PREFIX
: the Home Assistant discovery prefix to use
(default: homeassistant
)ECOWITT2MQTT_HASS_DISCOVERY
: publish data in the Home Assistant MQTT Discovery format
(default: false
)ECOWITT2MQTT_HASS_ENTITY_ID_PREFIX
: the prefix to use for Home Assistant entity IDs
(default: ""
)ECOWITT2MQTT_INPUT_DATA_FORMAT
: the input data format used by the gateway (default:
ecowitt
)ECOWITT2MQTT_INPUT_UNIT_SYSTEM
: the input unit system used by the device (default:
imperial
)ECOWITT2MQTT_LOCALE
: the locale to use (default: en_US.UTF-8
)ECOWITT2MQTT_MQTT_BROKER
: the hostname or IP address of an MQTT brokerECOWITT2MQTT_MQTT_PASSWORD
: a valid password for the MQTT brokerECOWITT2MQTT_MQTT_PORT
: the listening port of the MQTT broker (default: 1883
)ECOWITT2MQTT_MQTT_RETAIN
: whether to instruct the MQTT broker to retain messages
(default: false
)ECOWITT2MQTT_MQTT_TLS
: publish data via MQTT over TLS (default: false
)ECOWITT2MQTT_MQTT_TOPIC
: the MQTT topic to publish device data toECOWITT2MQTT_MQTT_USERNAME
: a valid username for the MQTT brokerECOWITT2MQTT_OUTPUT_UNIT_SYSTEM
: the unit system to use in output (default: imperial
)ECOWITT2MQTT_OUTPUT_UNIT_TEMPERATURE
: the output unit to use for temperature data
points (default: the default used by the output unit system)ECOWITT2MQTT_PORT
: the port to serve ecowitt2mqtt on (default: 8080
)ECOWITT2MQTT_PRECISION
: the precision to output data points at (default: no limit)ECOWITT2MQTT_RAW_DATA
: return raw data (don't attempt to translate any values)
(default: false
)ECOWITT2MQTT_VERBOSE
: increase verbosity of logged output (default: false
)The configuration file can be formatted as either YAML:
---
battery_override:
battery_key1: boolean
boolean_battery_true_value: 1
default_battery_strategy: numeric
diagnostics: false
disable_calculated_data: false
endpoint: /data/report
hass_discovery: false
hass_discovery_prefix: homeassistant
hass_entity_id_prefix: test_prefix
input_data_format: ecowitt
input_unit_system: imperial
locale: en_US.UTF-8
mqtt_broker: 127.0.0.1
mqtt_password: password
mqtt_port: 1883
mqtt_retain: false
mqtt_tls: false
mqtt_topic: Test
mqtt_username: user
output_unit_system: imperial
port: 8080
raw_data: false
verbose: false
...or JSON
{
"battery_override": {
"battery_key1": "boolean"
},
"boolean_battery_true_value": 1,
"default_battery_strategy": "numeric",
"diagnostics": false,
"disable_calculated_data": false,
"endpoint": "/data/report",
"hass_discovery": false,
"hass_discovery_prefix": "homeassistant",
"hass_entity_id_prefix": "test_prefix"
"input_data_format": "ecowitt",
"input_unit_system": "imperial",
"locale": "en_US.UTF-8",
"mqtt_broker": "127.0.0.1",
"mqtt_password": "password",
"mqtt_port": 1883,
"mqtt_retain": true,
"mqtt_tls": false,
"mqtt_topic": "Test",
"mqtt_username": "user",
"output_unit_system": "imperial",
"port": 8080,
"raw_data": false,
"verbose": false
}
...and makes use of the same config options available to the CLI.
When using the configuration file, it is possible to define specific configuration parameters for multiple Ecowitt gateways. This is useful if different gateways should publish to different MQTT brokers, in different formats, etc.
First, you must determine the unique ID for each gateway. This can be observed in the
logs when verbose
is enabled – look for the PASSKEY
value that the gateway has:
Received data from the Ecowitt device: {'PASSKEY': 'abcde12345', ...}
Then, in the configuration file, simply add a gateways
key that contains a mapping of
any of the existing configuration options (except for --verbose
and --diagnostics
,
which can only be defined once and are applied to every configuration). Options that
remain at the root level of the file are treated as defaults.
For example, this YAML configuration file:
---
mqtt_broker: 127.0.0.1
mqtt_password: password
mqtt_topic: Test
mqtt_username: user
gateways:
abcde12345:
hass_discovery: true
...defines two gateway definitions:
Test
topic on an MQTT broker at 127.0.0.1
PASSKEY
of abcde12345
) that publishes to the same broker, but in Home
Assistant MQTT Discovery format.In another example, this JSON configuration file:
{
"mqtt_broker": "127.0.0.1",
"mqtt_password": "password",
"mqtt_port": 1883,
"mqtt_topic": "Test",
"mqtt_username": "user",
"gateways": {
"abcde12345": {
"mqtt_broker": "192.168.1.100",
"mqtt_retain": true,
"output_unit_system": "metric"
}
}
}
...defines two gateway definitions:
Test
topic on an MQTT broker at 127.0.0.1
PASSKEY
of abcde12345
) that publishes to a different broker
(192.168.1.100
), outputs the data in metric, and retains the data on the brokerWhen parsing configuration options, ecowitt2mqtt
looks at the configuration sources in
the following order:
This allows you to mix and match sources – for instance, you might have "defaults" in the configuration file and override them via environment variables.
ecowitt2mqtt
currently supports the following input data formats:
ambient_weather
ecowitt
wunderground
(Weather Underground)Provide the correct one to ---input-data-format
based on which device brand you use.
In addition to the data coming from a gateway, ecowitt2mqtt
will automatically deduce
and published several additional, calculated data points if the requisite underlying
data exists:
frostpoint
)(Special thanks to the excellent thermal_comfort
library for
inspiration on many of these.)
If you would prefer to not have these sensors calculated and published, you can utilize
the --disable-calculated-data
configuration option.
Ecowitt devices report battery levels in three different formats:
boolean
: 0
represents OFF
(i.e., the battery is in normal condition) and 1
represents
ON
(i.e., the battery is low).numeric
: the raw numeric value is interpreted as the number of volts remaining in the
battery.percentage
: the raw numeric value is interpreted as the percentage of voltage
remaining the battery.ecowitt2mqtt
provides three mechanisms to handle this complexity:
ecowitt2mqtt
contains an internal mapping that should automatically transform all
known battery types into their correct format.
By using the --default-battery-strategy
configuration parameter, users can specify how
unknown battery types should be treated by default.
Individual batteries can be overridden and given a new strategy. How this is accomplished differs slightly based on the configuration method used:
--battery-override "batt1=boolean"
optionsECOWITT2MQTT_BATTERY_OVERRIDE
variable that is a
semicolon-delimited pair of "key=value" strings (e.g.,
ECOWITT2MQTT_BATTERY_OVERRIDE="batt1=boolean;batt2=numeric"
)These overrides work on both known and unknown battery types; that said, if you should
find the need to override a known battery type because ecowitt2mqtt
has an incorrect
internal interpretation, submit an issue to get it corrected!
In this example, a user mostly has batteries that should be treated as boolean
, but
also has one – wh60_batt1
– that should be treated as numeric.
$ ecowitt2mqtt --default-battery-strategy boolean --battery-override="wh60_batt1=numeric"
$ ECOWITT2MQTT_DEFAULT_BATTERY_STRATEGY=boolean \
ECOWITT2MQTT_BATTERY_OVERRIDE="wh60_batt1=numeric" \
ecowitt2mqtt
In YAML:
---
default_battery_strategy: boolean
battery_override:
wh60_batt1: numeric
...or JSON
{
"default_battery_strategy": "boolean",
"battery_override": {
"wh60_batt1": "numeric"
}
}
ecowitt2mqtt
allows you to specify both the input and output unit systems for a device
via the --input-unit-system
and --output-unit-system
config options, respectively.
These are fairly self-explanatory, but take care to use an --input-unit-system
that is
consistent with what your device provides (otherwise, your data will be very "off").
If you wish, you can change the unit for individual data categories. For example, let's
say you wanted to use metric for the output unit system, but wanted to represent all
temperature data points in Kelvin – you would provide --output-unit-system=metric
and
--output-unit-temperature=K
as config options. As expected, the value is properly
converted to the new unit.
Config Option: --output-unit-accumulated-precipitation
in
mm
Config Option: --output-unit-humidity
g/m³
lbs/ft³
Config Option: --output-unit-illuminance
fc
kfc
klx
lx
W/m²
Config Option: --output-unit-precipitation-rate
in/h
mm/h
Config Option: --output-unit-pressure
bar
cbar
hPa
inHg
kPa
mbar
mmHg
Pa
psi
Config Option: --output-unit-speed
ft/s
in/d
in/h
km/h
kn
m/s
mph
mm/d
Config Option: --output-unit-temperature
°C
°F
K
In some cases, it may be preferable to prevent ecowitt2mqtt
from doing any data
translation (converting values to a new unit system, changing binary values – such as
might be used by a battery – into "friendly" values, etc.). Passing the --raw-data
flag
will accomplish this: data will flow directly from the Ecowitt device to the MQTT broker
as-is.
Note that the --raw-data
flag supersedes any that might cause data translation (such as
--input-unit-system
or --output-unit-system
).
Home Assistant users can quickly add entities from an Ecowitt device
by using MQTT Discovery. Once Home Assistant is
configured to accept MQTT Discovery, ecowitt2mqtt
simply needs the --hass-discovery
flag:
$ ecowitt2mqtt \
--mqtt-broker=192.168.1.101 \
--mqtt-username=user \
--mqtt-password=password \
--hass-discovery
You can provide a custom prefix for all Home Assistant entities via the
--hass-entity-id-prefix
config parameter.
Home Assistant OS users can install the official ecowitt2mqtt
add-on by clicking the
link below:
ecowitt2mqtt
doesn't, itself, provide any sort of daemonization mechanism. The suggested
route is to use a different application.
supervisord
An example supervisord
configuration file might look like this:
[supervisord]
nodaemon=true
loglevel=info
user=root
[program:ecowitt2mqtt]
command=ecowitt2mqtt --mqtt-broker=192.168.1.101 --mqtt-username=user --mqtt-password=password
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true
systemd
An example systemd
service file in /etc/systemd/system
might look like this:
[Unit]
Description=ECOWITT2MQTT daemon
After=network.target
[Service]
Type=simple
ExecStart=ecowitt2mqtt --mqtt-broker=192.168.1.101 --mqtt-username=user --mqtt-password=password
ExecReload=kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
To enable the service:
$ systemctl enable ecowitt2mqtt
The library is available via a Docker image from both Docker Hub and ghcr.io. It is configured by using the same environment variables listed above.
Running the image is straightforward:
docker run -it \
-e ECOWITT2MQTT_MQTT_BROKER=192.168.1.101 \
-e ECOWITT2MQTT_MQTT_USERNAME=user \
-e ECOWITT2MQTT_MQTT_PASSWORD=password \
-e ECOWITT2MQTT_MQTT_TOPIC=topic \
-p 8080:8080 \
bachya/ecowitt2mqtt:latest
Note the value of the -p
flag: you must expose the port defined by the PORT
environment variable. In the example above, the default port (8080
) is used and is
exposed via the same port on the host.
docker-compose
users can find an example configuration file at
docker-compose.dev.yml
. Note that this is intended to be a dev
environment for quickly testing the repo itself; in production, you should refer to one
of the published images.
You may run ecowitt2mqtt
in diagnostics mode by providing the --diagnostics
flag. In
this mode, the app will wait until it receives and publishes a single payload, then
exit. This allows users to collect a small-but-complete payload for use in testing,
debugging, and issue reporting.
Thanks to all of our contributors so far!
python3 -m venv .venv
source ./.venv/bin/activate
script/setup
poetry run pytest --cov ecowitt2mqtt tests
README.md
with any new documentation.FAQs
A small web server to send data from Ecowitt devices to an MQTT Broker
We found that ecowitt2mqtt demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.