{"id":294194,"date":"2026-08-29T08:58:19","date_gmt":"2026-08-29T08:58:19","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/xtx-netatmo\/"},"modified":"2026-08-31T17:22:24","modified_gmt":"2026-08-31T17:22:24","slug":"xtx-integration-for-netatmo","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/xtx-integration-for-netatmo\/","author":15516942,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.9.9","stable_tag":"1.9.9","tested":"7.1","requires":"6.2","requires_php":"8.0","requires_plugins":null,"header_name":"XTX Integration for Netatmo","header_author":"Frank Neumann","header_description":"Connects to the Netatmo API, stores all sensor data locally and displays live dashboards, charts, history and forecasts via shortcodes.","assets_banners_color":"284c4e","last_updated":"2026-08-31 17:22:24","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/netatmo.frank-neumann.de\/","header_author_uri":"https:\/\/frank-neumann.de","rating":0,"author_block_rating":0,"active_installs":0,"downloads":100,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.9.6.2":{"tag":"1.9.6.2","author":"xylaender","date":"2026-08-29 08:57:55","revision":3671275},"1.9.7":{"tag":"1.9.7","author":"xylaender","date":"2026-08-29 10:50:58","revision":3671421},"1.9.8":{"tag":"1.9.8","author":"xylaender","date":"2026-08-31 17:10:14","revision":3674682},"1.9.9":{"tag":"1.9.9","author":"xylaender","date":"2026-08-31 17:22:24","revision":3674713}},"upgrade_notice":{"1.9.7":"<p>First release published through the WordPress.org directory. Breaking change for REST API users: the key is accepted in the X-NAWS-Key header only, and ?api_key=... now answers 401. Everything else is new features and fixes.<\/p>","1.9.6.2":"<p>Header correction only: compatibility is now declared against WordPress 7.1. Identical in code to 1.9.6.1.<\/p>","1.9.6.1":"<p>Security release. The OAuth return route now requires the manage_options capability before it exchanges an authorization code for tokens. Reported by the WordPress plugin review team. Updating is recommended for every installation.<\/p>","1.6.3":"<p>WordPress.org compliance release. All file-scope ob_start() patterns replaced with wp_add_inline_script(). PHP values passed to JS via wp_json_encode() instead of direct echoing.<\/p>","1.6.2":"<p>WordPress.org compliance release. Input sanitization, ob_start() fixes, inline script\/style removal, SQL whitelist validation.<\/p>","1.5.7":"<p>WordPress.org compliance release. Removed GitHub Auto-Updater. Plugin Check fixes for move_uploaded_file, rand, SVG escaping.<\/p>","1.5.6":"<p>Security update: Client ID and Client Secret are now fully AES-256-GCM encrypted at rest.<\/p>","1.4.3":"<p>Plugin renamed to &quot;XTX Netatmo&quot;. New icon sets and per-sensor colors.<\/p>","1.4.0":"<p>Major visual update: 130+ configurable colors with live preview on new Appearance page.<\/p>","1.3.0":"<p>New Export \/ Import feature for full data backup and migration.<\/p>","1.2.0":"<p>Complete mobile-first responsive redesign. All views optimized for smartphones.<\/p>","1.1.0":"<p>Error logging, caching, adaptive polling, night mode and health dashboard.<\/p>","1.0.2":"<p>Removed shortcodes: naws_chart, naws_gauge, naws_dashboard, naws_card. Use [naws_live] and [naws_history] instead.<\/p>","1.0.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3671348,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3671348,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3671348,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500-de.png":{"filename":"banner-1544x500-de.png","revision":3671363,"resolution":"1544x500","location":"assets","locale":"de","width":1544,"height":500},"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3671348,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250-de.png":{"filename":"banner-772x250-de.png","revision":3671363,"resolution":"772x250","location":"assets","locale":"de","width":772,"height":250},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3671348,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.9.6.2","1.9.7","1.9.8","1.9.9"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3671348,"resolution":"1","location":"assets","locale":"","width":2600,"height":3700},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3671348,"resolution":"2","location":"assets","locale":"","width":2600,"height":4900},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3671348,"resolution":"3","location":"assets","locale":"","width":3000,"height":2048},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3671348,"resolution":"4","location":"assets","locale":"","width":3000,"height":2650},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3671348,"resolution":"5","location":"assets","locale":"","width":1116,"height":932},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3671348,"resolution":"6","location":"assets","locale":"","width":3000,"height":2640},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3671348,"resolution":"7","location":"assets","locale":"","width":3000,"height":1256}},"screenshots":{"1":"Live dashboard with sensor cards and 24h trend charts","2":"Year-over-year comparison charts for temperature and rainfall","3":"Admin settings page with Netatmo connection status","4":"REST API documentation in the admin panel","5":"Weather forecast widget","6":"Appearance page with live color preview","7":"Export \/ Import page for backups"}},"plugin_section":[],"plugin_tags":[2510,40864,7877,4899,269299],"plugin_category":[59],"plugin_contributors":[278161],"plugin_business_model":[],"class_list":["post-294194","plugin","type-plugin","status-publish","hentry","plugin_tags-chart","plugin_tags-netatmo","plugin_tags-temperature","plugin_tags-weather","plugin_tags-weather-station","plugin_category-utilities-and-tools","plugin_contributors-xylaender","plugin_committers-xylaender"],"banners":{"banner":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/banner-772x250.png?rev=3671348","banner_2x":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/banner-1544x500.png?rev=3671348","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/icon.svg?rev=3671348","icon":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/icon.svg?rev=3671348","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/screenshot-1.png?rev=3671348","caption":"Live dashboard with sensor cards and 24h trend charts"},{"src":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/screenshot-2.png?rev=3671348","caption":"Year-over-year comparison charts for temperature and rainfall"},{"src":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/screenshot-3.png?rev=3671348","caption":"Admin settings page with Netatmo connection status"},{"src":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/screenshot-4.png?rev=3671348","caption":"REST API documentation in the admin panel"},{"src":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/screenshot-5.png?rev=3671348","caption":"Weather forecast widget"},{"src":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/screenshot-6.png?rev=3671348","caption":"Appearance page with live color preview"},{"src":"https:\/\/ps.w.org\/xtx-integration-for-netatmo\/assets\/screenshot-7.png?rev=3671348","caption":"Export \/ Import page for backups"}],"raw_content":"<!--section=description-->\n<p><strong>XTX Integration for Netatmo<\/strong> connects your Netatmo hardware to WordPress. It reads all sensor data via the official Netatmo API, stores readings in your local database and displays them with beautiful live dashboards, animated charts and weather forecasts.<\/p>\n\n<h4>Key Features<\/h4>\n\n<ul>\n<li><strong>Full Netatmo Integration<\/strong> \u2013 OAuth2 authentication, automatic sync, all module types supported (Base, Outdoor, Wind, Rain, Indoor)<\/li>\n<li><strong>Live Dashboard<\/strong> \u2013 Real-time sensor cards with animated counters, 24h trend charts, pressure trend indicator, wind compass, CO2 air quality levels<\/li>\n<li><strong>Astronomy<\/strong> \u2013 Sunrise\/sunset, moon phase with illumination, next full moon<\/li>\n<li><strong>Derived Weather Data<\/strong> \u2013 Feels-like temperature, heat index, dew point, wind chill<\/li>\n<li><strong>Historical Charts<\/strong> \u2013 Year-over-year comparison for temperature, pressure and rainfall with interactive legend<\/li>\n<li><strong>Weather Forecast<\/strong> \u2013 5-day forecast based on station coordinates via Open-Meteo or Yr.no<\/li>\n<li><strong>REST API<\/strong> \u2013 Read-only JSON API with key authentication and rate limiting for external tools (Google Charts, Grafana, etc.)<\/li>\n<li><strong>Encrypted Storage<\/strong> \u2013 All credentials (OAuth tokens, client secret, API keys) are AES-256-GCM encrypted at rest<\/li>\n<li><strong>Configurable Units<\/strong> \u2013 C\/F, mm\/inch, mbar\/inHg\/mmHg, km\/h\/m\/s\/mph\/kn<\/li>\n<li><strong>Multilingual<\/strong> \u2013 Full German, English and Norwegian interface<\/li>\n<li><strong>10 Shortcodes<\/strong> \u2013 Dashboard, current readings, infobar, single value, computed value, history charts, forecast, table, widget, weather icon<\/li>\n<li><strong>Export \/ Import<\/strong> \u2013 Full backup and restore of weather data, modules and settings<\/li>\n<li><strong>Mobile-First Responsive<\/strong> \u2013 All views optimized for smartphones, tablets and desktops<\/li>\n<li><strong>130+ Configurable Colors<\/strong> \u2013 Full appearance customization with live preview<\/li>\n<li><strong>4 Icon Sets<\/strong> \u2013 Emoji, Outline, Filled, Minimal with per-sensor color control<\/li>\n<\/ul>\n\n<h4>Supported Modules<\/h4>\n\n<ul>\n<li><strong>NAMain<\/strong> \u2013 Base Station (Temperature, Humidity, CO2, Noise, Pressure)<\/li>\n<li><strong>NAModule1<\/strong> \u2013 Outdoor (Temperature, Humidity)<\/li>\n<li><strong>NAModule2<\/strong> \u2013 Wind (Speed, Direction, Gusts)<\/li>\n<li><strong>NAModule3<\/strong> \u2013 Rain Gauge (Hourly, Daily, Rolling 24h)<\/li>\n<li><strong>NAModule4<\/strong> \u2013 Additional Indoor (Temperature, Humidity, CO2)<\/li>\n<\/ul>\n\n<h4>Shortcodes<\/h4>\n\n<ul>\n<li><code>[naws_live]<\/code> \u2013 Live sensor tiles with 24h trend charts and forecast<\/li>\n<li><code>[naws_current]<\/code> \u2013 Current readings of one or all modules as tiles or a list (<code>module_id<\/code>, <code>parameters<\/code>, <code>layout<\/code>, <code>title<\/code>)<\/li>\n<li><code>[naws_infobar]<\/code> \u2013 Astronomy bar with sunrise, moon phase, felt temperature<\/li>\n<li><code>[naws_value]<\/code> \u2013 Single inline sensor value<\/li>\n<li><code>[naws_calc]<\/code> \u2013 Single computed value (dew point, felt temperature, sunrise, moon phase, \u2026); full list on the Shortcodes page in the backend<\/li>\n<li><code>[naws_history]<\/code> \u2013 Year-over-year comparison charts (supports <code>year<\/code> parameter)<\/li>\n<li><code>[naws_forecast]<\/code> \u2013 Multi-day weather forecast<\/li>\n<li><code>[naws_table]<\/code> \u2013 Readings as a table over a period, grouped by hour, day, week, month or year (<code>module_id<\/code>, <code>parameters<\/code>, <code>period<\/code>, <code>limit<\/code>, <code>group_by<\/code>, <code>title<\/code>)<\/li>\n<li><code>[naws_weather_widget]<\/code> \u2013 Compact forecast widget for a sidebar (<code>days<\/code> 3 or 5, <code>width<\/code> 250\u2013500)<\/li>\n<li><code>[naws_weather_icon]<\/code> \u2013 Just the animated icon for the current weather state (<code>size<\/code>); renders nothing when the state is unknown<\/li>\n<\/ul>\n\n<h3>Privacy &amp; External Services<\/h3>\n\n<p>This plugin connects to the following external services:<\/p>\n\n<h4>Netatmo API (api.netatmo.com)<\/h4>\n\n<ul>\n<li><strong>Purpose:<\/strong> Authenticate via OAuth2, fetch sensor readings and station data<\/li>\n<li><strong>Data sent:<\/strong> The Client ID and Client Secret of the Netatmo application you created, in exchange for an access token; afterwards the access or refresh token with every request, plus the station and module IDs whose measurements are being requested<\/li>\n<li><strong>When:<\/strong> During initial authentication, on every automatic sync cycle, on every token refresh, and while a historical import is running<\/li>\n<li><strong>Terms of service:<\/strong> <a href=\"https:\/\/dev.netatmo.com\/legal\">https:\/\/dev.netatmo.com\/legal<\/a><\/li>\n<li><strong>Privacy policy:<\/strong> <a href=\"https:\/\/legals.netatmo.com\/?goto=privacy\">https:\/\/legals.netatmo.com\/?goto=privacy<\/a><\/li>\n<\/ul>\n\n<h4>Open-Meteo API (api.open-meteo.com)<\/h4>\n\n<ul>\n<li><strong>Purpose:<\/strong> Fetch weather forecast data based on station coordinates (default provider)<\/li>\n<li><strong>Data sent:<\/strong> Latitude and longitude of your weather station<\/li>\n<li><strong>When:<\/strong> When the forecast shortcode is displayed (cached for 3 hours)<\/li>\n<li><strong>Terms and privacy:<\/strong> <a href=\"https:\/\/open-meteo.com\/en\/terms\">https:\/\/open-meteo.com\/en\/terms<\/a><\/li>\n<li><strong>Note:<\/strong> Open-Meteo is a free, open-source weather API. No API key or registration required.<\/li>\n<\/ul>\n\n<h4>Open-Meteo Geocoding API (geocoding-api.open-meteo.com)<\/h4>\n\n<ul>\n<li><strong>Purpose:<\/strong> Turn a place into coordinates, and coordinates into a place name for the forecast heading<\/li>\n<li><strong>Data sent:<\/strong> In \"manual\" location mode, the city name or postal code entered in the plugin settings. In \"automatic\" mode, the latitude and longitude of your weather station, rounded to two decimal places, in order to look up the name of the nearest place.<\/li>\n<li><strong>When:<\/strong> In manual mode whenever no cached result exists (cached for 7 days). In automatic mode exactly once \u2014 the resolved name is stored in the plugin settings and never looked up again.<\/li>\n<li><strong>Terms and privacy:<\/strong> <a href=\"https:\/\/open-meteo.com\/en\/terms\">https:\/\/open-meteo.com\/en\/terms<\/a><\/li>\n<li><strong>Documentation:<\/strong> <a href=\"https:\/\/open-meteo.com\/en\/docs\/geocoding-api\">https:\/\/open-meteo.com\/en\/docs\/geocoding-api<\/a><\/li>\n<\/ul>\n\n<h4>Yr.no \/ MET Norway API (api.met.no)<\/h4>\n\n<ul>\n<li><strong>Purpose:<\/strong> Fetch weather forecast data (optional provider, selectable in settings)<\/li>\n<li><strong>Data sent:<\/strong> Latitude and longitude of your weather station<\/li>\n<li><strong>When:<\/strong> When the forecast shortcode is displayed and Yr.no is selected as provider (cached for 3 hours)<\/li>\n<li><strong>Privacy policy:<\/strong> <a href=\"https:\/\/www.met.no\/en\/About-us\/privacy\">https:\/\/www.met.no\/en\/About-us\/privacy<\/a><\/li>\n<li><strong>Terms:<\/strong> <a href=\"https:\/\/developer.yr.no\/doc\/TermsOfService\/\">https:\/\/developer.yr.no\/doc\/TermsOfService\/<\/a><\/li>\n<li><strong>Note:<\/strong> Free API, no API key needed. MET Norway's terms require every client to identify itself, so requests to this service carry a User-Agent naming the plugin, its version and your site address \u2014 that address is how MET Norway would reach you before restricting a misbehaving client. This is sent to api.met.no only, and only while Yr.no is the selected provider.<\/li>\n<\/ul>\n\n<p>No personal user data (names, emails, IP addresses) is collected or transmitted by this plugin. All sensor data is stored exclusively in your local WordPress database.<\/p>\n\n<h3>Third-Party Libraries<\/h3>\n\n<p>Two JavaScript libraries are bundled with this plugin, both under the MIT license, which is GPL-compatible. They ship in their minified distribution builds; the unminified source and the build tooling for each are available at the links below.<\/p>\n\n<h4>Chart.js 4.5.1<\/h4>\n\n<ul>\n<li><strong>File:<\/strong> <code>assets\/vendor\/chart.umd.min.js<\/code><\/li>\n<li><strong>License:<\/strong> MIT<\/li>\n<li><strong>Homepage:<\/strong> <a href=\"https:\/\/www.chartjs.org\">https:\/\/www.chartjs.org<\/a><\/li>\n<li><strong>Source and build tools:<\/strong> <a href=\"https:\/\/github.com\/chartjs\/Chart.js\">https:\/\/github.com\/chartjs\/Chart.js<\/a> \u2014 the exact release bundled here is <a href=\"https:\/\/github.com\/chartjs\/Chart.js\/releases\/tag\/v4.5.1\">v4.5.1<\/a><\/li>\n<li><strong>Used for:<\/strong> All charts \u2014 24h trend lines on the live dashboard and the year-over-year history charts<\/li>\n<\/ul>\n\n<h4>chartjs-adapter-date-fns 3.0.0<\/h4>\n\n<ul>\n<li><strong>File:<\/strong> <code>assets\/vendor\/chartjs-adapter-date-fns.bundle.min.js<\/code><\/li>\n<li><strong>License:<\/strong> MIT<\/li>\n<li><strong>Source and build tools:<\/strong> <a href=\"https:\/\/github.com\/chartjs\/chartjs-adapter-date-fns\">https:\/\/github.com\/chartjs\/chartjs-adapter-date-fns<\/a> \u2014 the exact release bundled here is <a href=\"https:\/\/github.com\/chartjs\/chartjs-adapter-date-fns\/releases\/tag\/v3.0.0\">v3.0.0<\/a><\/li>\n<li><strong>Used for:<\/strong> Time axis formatting in the charts. This is the bundled build, which includes date-fns (also MIT).<\/li>\n<\/ul>\n\n<p>No other third-party code is included. No library is loaded from a CDN; everything is served from your own installation. Libraries that ship with WordPress itself are used from WordPress and are not bundled.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>xtx-integration-for-netatmo<\/code> folder to <code>\/wp-content\/plugins\/<\/code><\/li>\n<li>Activate the plugin in WordPress Admin &gt; Plugins<\/li>\n<li>Go to <strong>XTX Netatmo &gt; Settings<\/strong><\/li>\n<li>Create a Netatmo developer app at <a href=\"https:\/\/dev.netatmo.com\">dev.netatmo.com<\/a><\/li>\n<li>Enter your Client ID and Client Secret<\/li>\n<li>Set the Redirect URI in your Netatmo app to: <code>https:\/\/yoursite.com\/wp-admin\/admin.php?page=naws-settings<\/code><\/li>\n<li>Click \"Connect to Netatmo\" and authorize<\/li>\n<li>Data syncs automatically \u2013 add shortcodes to any page<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"how%20do%20i%20get%20netatmo%20api%20credentials%3F\"><h3>How do I get Netatmo API credentials?<\/h3><\/dt>\n<dd><p>Visit <a href=\"https:\/\/dev.netatmo.com\">dev.netatmo.com<\/a>, log in with your Netatmo account, create a new application and copy the Client ID and Client Secret.<\/p><\/dd>\n<dt id=\"how%20often%20does%20data%20update%3F\"><h3>How often does data update?<\/h3><\/dt>\n<dd><p>Netatmo sensors transmit every 5 minutes. The plugin sync interval is configurable (5\u20131440 minutes). Night mode reduces polling between 23:00\u201306:00.<\/p><\/dd>\n<dt id=\"can%20i%20import%20historical%20data%3F\"><h3>Can I import historical data?<\/h3><\/dt>\n<dd><p>Yes. The plugin includes a chunk-based historical importer that fetches data from the Netatmo getmeasure API without hitting rate limits.<\/p><\/dd>\n<dt id=\"is%20the%20rest%20api%20secure%3F\"><h3>Is the REST API secure?<\/h3><\/dt>\n<dd><p>Yes. Every endpoint is read-only, rate-limited, and requires an API key generated in the admin panel. The key is accepted in the X-NAWS-Key header only \u2014 never as a query parameter, so it cannot end up in access logs, the Referer header, browser history or a proxy along the way.<\/p><\/dd>\n<dt id=\"are%20my%20netatmo%20credentials%20safe%3F\"><h3>Are my Netatmo credentials safe?<\/h3><\/dt>\n<dd><p>All sensitive data (OAuth tokens, client ID, client secret, API keys) is encrypted with AES-256-GCM before being stored in the database.<\/p><\/dd>\n<dt id=\"can%20i%20customize%20the%20appearance%3F\"><h3>Can I customize the appearance?<\/h3><\/dt>\n<dd><p>Yes. The Appearance page offers 130+ configurable colors with live preview, 4 icon sets, per-sensor colors, chart theming and year comparison palettes.<\/p><\/dd>\n<dt id=\"can%20i%20back%20up%20my%20weather%20data%3F\"><h3>Can I back up my weather data?<\/h3><\/dt>\n<dd><p>Yes. The Export\/Import feature lets you download weather data, module configs and all settings as JSON. Ideal for migrating to a new WordPress installation.<\/p><\/dd>\n<dt id=\"which%20forecast%20providers%20are%20supported%3F\"><h3>Which forecast providers are supported?<\/h3><\/dt>\n<dd><p>Open-Meteo (global, default) and Yr.no \/ MET Norway (optimized for Northern Europe). Both are free and require no API key.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.9.9<\/h4>\n\n<ul>\n<li>Changed: <strong>breaking<\/strong> \u2014 the plugin no longer has its own language setting; the WordPress locale decides. The old setting was a single site-wide value for the front end and the back end at once, and it read the site language rather than your own, so it could never give you a back end in one language and your visitors another. Site Language plus the per-user Language in your profile do exactly that, and for your theme and every other plugin at the same time. If you had the plugin set to a language other than your site's, set the site language instead \u2014 or your own user language, if you meant only your own screen.<\/li>\n<li>Changed: the interface translates through WordPress now instead of through the plugin's own language files. Until now translate.wordpress.org saw six strings of this plugin; it sees all 649. That means anyone can contribute a language without touching the code, and every language gets a proper WordPress language pack.<\/li>\n<li>Changed: weekday, month and weather-condition names are translatable. They used to be two hardcoded lists, German and English, so a Norwegian reader got English with no way to change it.<\/li>\n<li>New: German and Norwegian ship with this release as a bridge. Language packs do not exist the moment an update goes out, and an installation that had a German interface yesterday should not find an English one today. A pack always takes precedence once it is built.<\/li>\n<\/ul>\n\n<h4>1.9.8<\/h4>\n\n<ul>\n<li>Fix: <code>[naws_table]<\/code> produced nothing at all. The shortcode was registered, documented in the reference and listed in this readme, and its stylesheet was written \u2014 but the template it includes, <code>templates\/table.php<\/code>, had never been committed. Every use of the shortcode output an empty string and left two PHP warnings in the log. The template is there now, and a test fails if it goes missing again.<\/li>\n<li>Fix: <code>[naws_table]<\/code> listed the bookkeeping values Netatmo stores alongside the readings. <code>max_wind_angle<\/code> and <code>max_wind_str<\/code> have neither a name nor a unit in the plugin, so they appeared as a raw key next to a bare number. The table now asks only for parameters it can present; naming one explicitly in <code>parameters<\/code> still returns it. The daily minimum and maximum temperature and the hourly rain total were in the same position for the opposite reason \u2014 they are meaningful and were simply unnamed, so they have their labels now instead of being hidden.<\/li>\n<li>Fix: <code>[naws_table]<\/code> found nothing for the periods its own reference recommends. <code>period=\"24h\"<\/code> asked for a range that starts tomorrow \u2014 PHP does not read <code>24h<\/code> as a duration, and <code>strtotime('-24h')<\/code> returns a point in the future rather than yesterday. <code>365d<\/code> failed to parse altogether. The start of the range therefore sat behind its end and the query returned nothing, which looked exactly like a station with no readings. The shorthand is spelled out before it reaches the parser now, and a period that cannot be read falls back to the documented 24 hours instead of to 1970.<\/li>\n<li>Fix: the reference gave the grouping of <code>[naws_table]<\/code> as hour, day, week or month. <code>year<\/code> works as well, and any other value lists single readings instead of averages. The readme named four of the shortcode's six attributes.<\/li>\n<\/ul>\n\n<h4>1.9.7<\/h4>\n\n<ul>\n<li>New: the order of the live cards and of the yearly comparison charts is a setting. Both lists on the Live-Dashboard screen are sortable by drag and drop, and the front end follows. A saved order decides position, never membership: an id left over from a renamed module is passed over, and a chart the order has never heard of takes its place at the end instead of disappearing.<\/li>\n<li>New: <code>[naws_calc]<\/code> \u2014 one shortcode for twenty-seven computed values in four kinds. Fourteen instant values (dew point, apparent temperature, wet-bulb temperature, heat index, thermal sensation, CO2 rating, wind compass, sunrise, sunset, day length, moon phase and illumination, next supermoon, next lunar eclipse), seven day classes with <code>mode=\"count|streak|max_streak\"<\/code> (ice days, frost days, summer days, hot days, tropical nights, heating and cooling days), five sums (heating and cooling degree days, growing degree days, the grassland temperature sum and the date the growing season started) and the Standardized Precipitation Index.<\/li>\n<li>New: the admin shows the state of the encryption \u2014 a missing openssl extension, a missing aes-256-gcm, an <code>AUTH_KEY<\/code> still set to the sample value from wp-config-sample.php, and changed WordPress salts. A fingerprint of the key sits beside the ciphertext so a salt change is recognizable as one instead of looking like a broken plugin.<\/li>\n<li>New: the header bar and the font are settings now. The font list offers only fonts your page already serves \u2014 the plugin loads no font file of its own.<\/li>\n<li>New: three settings for the degree-day limits: heating limit, room temperature and cooling limit.<\/li>\n<li>Changed: <strong>breaking<\/strong> \u2014 the REST API accepts its key in the <code>X-NAWS-Key<\/code> header only. A secret in the address line is written down by access logs, the Referer header, the browser history and every cache in between. Calls of the form <code>?api_key=...<\/code> now answer 401. If you use the API, switch before you update.<\/li>\n<li>Changed: the cron settings and the cron log now say what WP-Cron does not do. WP-Cron is triggered by page views, not by a clock, so on a quiet night no sync happens at all.<\/li>\n<li>Fix: a five-second hiccup at Netatmo cost a whole ten-minute polling cycle. Requests are retried now, and an error without an error code is no longer handed back as an empty result.<\/li>\n<li>Fix: a server without the openssl extension died on the first read of a token, and an unauthenticated REST request could turn a 401 into a fatal 500.<\/li>\n<li>Fix: the apparent temperature ignored wind chill, and the heat index was computed outside the range it is defined for.<\/li>\n<li>Fix: the next supermoon, the next lunar eclipse and the next full moon were dated in German whatever the site language was.<\/li>\n<li>Fix: an uppercase MAC address in <code>[naws_value module=\"...\"]<\/code> matched nothing, and an emptied degree-day limit field silently stored 0 degrees.<\/li>\n<li>Fix: this readme advertised five shortcodes and documented six. Ten are registered; all ten are listed now.<\/li>\n<li>Security: the REST API key page created and revoked keys on POST with a nonce check but no capability check of its own. The page is reachable only with <code>manage_options<\/code>, so nothing stood open \u2014 it is the pattern the review team warned about, and the check now runs before the nonce is spent.<\/li>\n<\/ul>\n\n<h4>1.9.6.2<\/h4>\n\n<ul>\n<li>Fix: the \"Tested up to\" header now names WordPress 7.1, in readme.txt and in the plugin header. No code changed from 1.9.6.1.<\/li>\n<\/ul>\n\n<h4>1.9.6.1<\/h4>\n\n<ul>\n<li>Security: the OAuth return route verified the OAuth state but not the caller's permission. <code>NAWS_Admin::handle_oauth_callback()<\/code> runs on <code>admin_init<\/code>, receives Netatmo's redirect and exchanged the code for an access and a refresh token \u2014 for whoever happened to be logged in. The state proves that the request belongs to a flow started on this site; it says nothing about who follows the redirect, and Netatmo returns the code to one fixed URL. <code>current_user_can( 'manage_options' )<\/code> now runs at the top of the method, before the state is read, so a request without the permission cannot consume the pending authorization either. Reported by the WordPress plugin review team.<\/li>\n<li>Security: removed a second acceptance path in the same check. A state that did not match was also accepted as <code>wp_verify_nonce( $state, 'naws_oauth' )<\/code> \u2014 a nonce that no code in the plugin ever created. The check is a single condition now, and a state that does not match ends the request.<\/li>\n<\/ul>\n\n<h4>1.9.6<\/h4>\n\n<ul>\n<li>Changed: the plugin no longer outputs any inline script. The boot code for the live dashboard and the history charts now ships as two ordinary JavaScript files that are enqueued the WordPress way. This also removes the reason the 1.9.3 workaround existed: an enqueued file cannot be silently dropped the way an inline fragment can. The JavaScript itself is unchanged, and several shortcodes on one page now share a single copy of it.<\/li>\n<\/ul>\n\n<h4>1.9.5<\/h4>\n\n<ul>\n<li>Fix: the User-Agent sent to the Yr.no \/ MET Norway forecast API named a repository that does not exist, and three other outgoing requests named no contact at all. MET Norway's terms require every client to identify itself with a reachable contact. All requests now send the plugin name, its version and your site address.<\/li>\n<li>Fix: the Netatmo privacy policy link in this readme was dead and has been replaced. The Netatmo entry also gained a terms of service link and now states that the Client ID and Client Secret are sent to the token endpoint.<\/li>\n<li>New: a Third-Party Libraries section documenting the two bundled JavaScript libraries (Chart.js and chartjs-adapter-date-fns), their MIT licenses and where to get their unminified source.<\/li>\n<li>New: the Open-Meteo geocoding service is now documented separately from the forecast service. It is a different host and had no entry of its own.<\/li>\n<li>Changed: the shipped LICENSE file is now GPL v2, matching the \"GPLv2 or later\" stated in the plugin header and this readme. The license itself is unchanged.<\/li>\n<li>Changed: removed a source map reference at the end of the bundled Chart.js file that pointed at a file the package never contained.<\/li>\n<\/ul>\n\n<h4>1.9.4<\/h4>\n\n<ul>\n<li>Changed: the database migration that adds the v1.4 sensor columns no longer assembles its SQL from variables. The column names came from a hardcoded list and were never user input, but Plugin Check's security scanner cannot verify that and flagged the query. Every ALTER TABLE statement is now written out in full, which leaves nothing to flag.<\/li>\n<li>Changed: that migration checked for each of its eight columns with a separate query. It reads the column list once now. The migration only runs on activation and upgrade, so this changes nothing you would notice.<\/li>\n<\/ul>\n\n<h4>1.9.3<\/h4>\n\n<ul>\n<li>Fix: the history charts stayed empty on some setups. The chart data and the chart script were injected with wp_add_inline_script(), which is silently dropped on those installations, so nothing at all was rendered. Both now use the same reliable pattern the live dashboard already used: a plain JSON data element plus a footer script block.<\/li>\n<li>Changed: the year buttons in the history charts sit below their chart instead of beside the title. They now use the full chart width and wrap over as many rows as needed, so stations with ten or more years of records no longer push the buttons out to the right. The enlarged chart view follows the same layout.<\/li>\n<li>Fix: hiding a year from the enlarged chart view left the corresponding button in the small chart looking active, even though the chart below had already dropped that year. Both legends are refreshed together now.<\/li>\n<\/ul>\n\n<h4>1.9.2<\/h4>\n\n<ul>\n<li>Fix: repeated API errors made the plugin poll harder rather than easing off. The interval is meant to double after three failures, but the ceiling on that calculation sat below the longest intervals the settings offer, so a 120-minute interval was halved and a 60-minute one never changed at all.<\/li>\n<li>Fix: a cron interval that is not one of the available schedules stopped polling completely. The field accepted any value from 5 to 1440 minutes while only seven of them exist as schedules, and an unlisted value such as 45 left the site with no fetch cron. The interval is a dropdown now, and stored values are snapped to the nearest schedule.<\/li>\n<li>Fix: night mode stopped reducing polling as soon as the API had trouble, because it measured from the last successful sync rather than the last attempt.<\/li>\n<li>Fix: the dashboard warned about a stale sync during normal night operation. The warning threshold now accounts for the doubled night interval.<\/li>\n<li>Changed: night mode, daily summaries and the history importer now use the timezone configured in WordPress instead of Europe\/Berlin. On sites in other timezones the night window fell on the wrong hours and daily boundaries could be cut at the wrong midnight.<\/li>\n<li>Changed: the error backoff is described under the interval setting rather than on the night mode checkbox. It applies regardless of night mode.<\/li>\n<\/ul>\n\n<h4>1.9.1<\/h4>\n\n<ul>\n<li>Changed: the settings screen has been reorganised. The connection card now spans the full width and the settings below sit in two balanced columns, so the forecast settings are visible near the top instead of at the bottom. The page is around 600 pixels shorter and no longer has a large empty area beside the connection card.<\/li>\n<li>Changed: one save button for all settings instead of three. The API credentials keep their own form and button.<\/li>\n<li>Fix: every save rewrote every setting. Each form carried hidden copies of the fields it did not own, a leftover from before the merge behaviour added in 1.7.0. Saving the units also rewrote the forecast settings and credentials from stale values, so edits made in a second browser tab could be lost. A save now only touches the fields shown in that form.<\/li>\n<li>Fix: the decrypted client secret was written into the page three times instead of once, and was submitted when saving unrelated settings.<\/li>\n<\/ul>\n\n<h4>1.9.0<\/h4>\n\n<ul>\n<li>New: the sidebar widget's width can be set between 250 and 500 pixels, in Appearance or per placement with <code>[naws_weather_widget width=\"400\"]<\/code>. Icon, figures and spacing scale with it \u2014 at 500 pixels the weather icon is 96 pixels instead of 64. The width acts as a maximum, so the widget still shrinks to fit a narrower column.<\/li>\n<li>Fix: the weather icon at the head of the sidebar widget was frozen. It was rendered through the same call as the small forecast icons, which are deliberately still, and inherited their frozen state. It now animates, while the forecast icons stay still as intended.<\/li>\n<\/ul>\n\n<h4>1.8.3<\/h4>\n\n<ul>\n<li>Fix: the Norwegian interface was only half translated. Norwegian arrived with 326 keys and the plugin has since grown to 612, so everything added later \u2014 the forecast, the REST API documentation, the shortcode reference and the live dashboard settings \u2014 stayed in English. All 272 missing strings are now translated into Bokmal.<\/li>\n<\/ul>\n\n<h4>1.8.2<\/h4>\n\n<ul>\n<li>Fix: the shortcode reference in the backend showed the wrong descriptions. A leftover block of duplicate entries in the language files overrode the detailed ones, and the entry for <code>[naws_history]<\/code> actually described <code>[naws_table]<\/code>.<\/li>\n<li>Security: input sanitization on the appearance settings form is now applied directly to the submitted data instead of through an intermediate variable, so automated review tools can see it. The data was sanitized before as well; only the form of the code changed.<\/li>\n<li>Changed: redirect URLs are built with RFC 3986 encoding.<\/li>\n<\/ul>\n\n<h4>1.8.1<\/h4>\n\n<ul>\n<li>Fix: the weather icon showed \"overcast\" under a cloudless sky. The forecast provider's weather code lumps all cloud layers together, so a thin veil of cirrus counted the same as a low, closed deck. Cloudiness is now read from the cover percentage per layer, with high cloud weighted down \u2014 a cirrus sky reads as fair, not overcast.<\/li>\n<li>Fix: the corrected cloud figures are also kept as the last known reading, so an outage of the forecast API no longer brings the old behaviour back.<\/li>\n<\/ul>\n\n<h4>1.8.0<\/h4>\n\n<ul>\n<li>New: sidebar widget <code>[naws_weather_widget]<\/code> \u2014 weather icon, outdoor temperature, rain and wind, plus a three- or five-day forecast, built for narrow columns<\/li>\n<li>New: forecast length selectable in the backend, with a live preview at the real width<\/li>\n<li>Changed: the colourful weather icons introduced in 1.7.0 are now used everywhere, including the forecast shortcode and the dashboard forecast strip<\/li>\n<li>Fix: saving settings on the Appearance page no longer redirects to Settings \u2014 forms now return to the page they were submitted from<\/li>\n<\/ul>\n\n<h4>1.7.0<\/h4>\n\n<ul>\n<li>New: animated weather icon with twelve states \u2014 shortcode <code>[naws_weather_icon size=\"96\"]<\/code> and, switchable in the backend, above the live dashboard<\/li>\n<li>New: the icon combines station readings with the forecast \u2014 your own measurements win, the forecast only fills in what the station cannot measure (cloud cover, thunderstorms, and precipitation if you have no rain gauge)<\/li>\n<li>New: rain-versus-snow is decided by wet-bulb temperature rather than air temperature, so snow is still recognised at 3\u20134 \u00b0C in dry air<\/li>\n<li>New: six thresholds in the settings (heavy rain, snow, fog humidity and spread, storm wind, dashboard placement)<\/li>\n<li>New: current conditions are fetched separately from the daily forecast, on a 30-minute cache, so the icon reflects the weather now instead of a whole-day summary<\/li>\n<li>Fix: saving one settings form no longer resets the others \u2014 saving your Netatmo credentials used to silently reset language, units, sync interval and all forecast settings back to their defaults<\/li>\n<li>Fix: REST API routes were not running their authentication callback \u2014 with the REST API enabled the endpoints, including station coordinates, were reachable without an API key<\/li>\n<\/ul>\n\n<h4>1.6.5<\/h4>\n\n<ul>\n<li>Compatibility: tested against WordPress 7.0<\/li>\n<li>Compatibility: minimum PHP requirement raised to 8.0 (<code>Requires PHP: 8.0<\/code>)<\/li>\n<li>Fix: history chart no longer emits a PHP 8.1 deprecation notice (<code>substr(): Passing null<\/code>) when the daily-summary table is still empty \u2014 the year range now falls back to the current year instead of year 0<\/li>\n<\/ul>\n\n<h4>1.6.4<\/h4>\n\n<ul>\n<li>Fix: <code>$file['size']<\/code> in import handler now wrapped in <code>intval()<\/code> for explicit sanitization (WordPress.org compliance)<\/li>\n<li>Fix: <code>SHOW COLUMNS<\/code> query in <code>class-naws-astro.php<\/code> now uses <code>$wpdb-&gt;prepare()<\/code> consistently with the rest of the codebase<\/li>\n<li>Fix: <code>naws_svg_kses_args()<\/code> helper properly available at plugin load time; resolves fatal error on admin dashboard when function was called before class-naws-helpers.php was loaded<\/li>\n<\/ul>\n\n<h4>1.6.3<\/h4>\n\n<ul>\n<li>WordPress.org compliance: all file-scope <code>ob_start()<\/code> \/ <code>ob_get_clean()<\/code> patterns removed from admin views and frontend templates<\/li>\n<li>WordPress.org compliance: PHP values injected into inline scripts via <code>wp_add_inline_script( $handle, $data, 'before' )<\/code> with <code>wp_json_encode()<\/code> instead of echoing PHP inside JS blocks<\/li>\n<li>WordPress.org compliance: all JS strings previously echoed from PHP now served via <code>nawsAdmin.strings<\/code> (localized with <code>wp_localize_script<\/code>) \u2013 eliminates PHP interpolation in JavaScript<\/li>\n<li>WordPress.org compliance: icon SVGs sanitized with <code>wp_kses()<\/code> before JSON-encoding; <code>NAWS_Icons::get_js_object()<\/code> (raw JS literal) replaced by <code>wp_json_encode( NAWS_Icons::get_set() )<\/code><\/li>\n<li>Added <code>ls_saving<\/code>, <code>ls_saved<\/code>, <code>ls_error<\/code> strings to <code>nawsAdmin<\/code> localization (live-settings page)<\/li>\n<\/ul>\n\n<h4>1.6.2<\/h4>\n\n<ul>\n<li>WordPress.org compliance: file upload sanitized with <code>sanitize_file_name()<\/code> and <code>move_uploaded_file()<\/code><\/li>\n<li>WordPress.org compliance: all <code>ob_start()<\/code> blocks closed with <code>ob_get_clean()<\/code> in same scope<\/li>\n<li>WordPress.org compliance: all remaining inline <code>&lt;script&gt;<\/code>\/<code>&lt;style&gt;<\/code> blocks converted to <code>wp_add_inline_script()<\/code> \/ <code>wp_add_inline_style()<\/code><\/li>\n<li>WordPress.org compliance: dynamic SQL column names validated against explicit whitelist before query execution<\/li>\n<li>Fix: <code>phpcs<\/code> annotation for <code>naws_appearance<\/code> input sanitization made explicit (sanitized via <code>NAWS_Colors::sanitize()<\/code>)<\/li>\n<\/ul>\n\n<h4>1.6.0<\/h4>\n\n<ul>\n<li>WordPress.org compliance: all inline <code>&lt;script&gt;<\/code> blocks converted to <code>wp_add_inline_script()<\/code><\/li>\n<li>WordPress.org compliance: all inline <code>&lt;style&gt;<\/code> blocks moved to enqueued stylesheet<\/li>\n<li>WordPress.org compliance: plugin renamed to \"XTX Integration for Netatmo\" (trademark)<\/li>\n<li>WordPress.org compliance: removed direct <code>&lt;script src&gt;<\/code> for Chart.js in frontend templates<\/li>\n<li>Updated Chart.js vendor from 4.4.0 to 4.5.1<\/li>\n<li>REST API docs: replaced Google Charts CDN example with bundled Chart.js example<\/li>\n<li>Fix: AJAX capability-check failures now return proper JSON 403 instead of plain <code>wp_die()<\/code><\/li>\n<li>Fix: privacy policy URL in readme.txt corrected<\/li>\n<\/ul>\n\n<h4>1.5.7<\/h4>\n\n<ul>\n<li>Removed GitHub Auto-Updater (WordPress.org compliance \u2013 hosted plugins must not include custom updaters)<\/li>\n<li>Fix: <code>move_uploaded_file()<\/code> replaced with <code>copy()<\/code> (WordPress Coding Standards)<\/li>\n<li>Fix: <code>rand()<\/code> replaced with <code>wp_rand()<\/code><\/li>\n<li>Fix: SVG output escaping documented with <code>phpcs:ignore<\/code><\/li>\n<li>Fix: 361 NonPrefixedVariableFound warnings resolved with scoped phpcs disable<\/li>\n<\/ul>\n\n<h4>1.5.6<\/h4>\n\n<ul>\n<li>Security: Client ID and Client Secret now AES-256-GCM encrypted at rest (all 5 secrets fully encrypted)<\/li>\n<li>Migration updated to encrypt plaintext credentials instead of forcing plaintext<\/li>\n<li>Removed legacy plaintext-enforcement from init<\/li>\n<\/ul>\n\n<h4>1.5.5<\/h4>\n\n<ul>\n<li>Fix: Escaping fixes for admin views (<code>esc_attr()<\/code> in modules.php, cron-log.php)<\/li>\n<li>Fix: readme.txt stable tag synchronized with plugin header version<\/li>\n<li>Fix: Option name inconsistency <code>naws_token_expires<\/code> unified to <code>naws_token_expiry<\/code><\/li>\n<\/ul>\n\n<h4>1.5.4<\/h4>\n\n<ul>\n<li>Fix: History chart year buttons now wrap on mobile instead of overflowing<\/li>\n<li>Fix: 24h chart modal overlay positioned correctly inside <code>.naws-wx<\/code> wrapper<\/li>\n<\/ul>\n\n<h4>1.5.3<\/h4>\n\n<ul>\n<li>Fix: Auto-update toggle now visible in WordPress plugin list (registered in <code>$transient-&gt;no_update<\/code>)<\/li>\n<\/ul>\n\n<h4>1.5.2<\/h4>\n\n<ul>\n<li>Fix: Plugin URI corrected to <code>https:\/\/www.frank-neumann.de\/netatmo-wetter-plugin\/<\/code><\/li>\n<\/ul>\n\n<h4>1.5.1<\/h4>\n\n<ul>\n<li>Fix: Dashboard SVG icons no longer rendered as raw source code<\/li>\n<\/ul>\n\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>New: GitHub Auto-Updater via <code>NAWS_Updater<\/code> class (later removed in 1.5.7 for WordPress.org compliance)<\/li>\n<\/ul>\n\n<h4>1.4.3<\/h4>\n\n<ul>\n<li>Plugin renamed: \"Netatmo Weather Station\" to \"XTX Netatmo\"<\/li>\n<li>New: 4 frontend icon sets (Emoji, Outline, Filled, Minimal) selectable in Appearance<\/li>\n<li>New: Per-sensor icon colors with live preview (7 configurable colors)<\/li>\n<li>New: Dynamic icon rendering via <code>NAWS_Icons<\/code> class<\/li>\n<\/ul>\n\n<h4>1.4.2<\/h4>\n\n<ul>\n<li>Fix: Forecast provider selection now works correctly (provider-aware cache keys)<\/li>\n<li>Fix: Forecast source label dynamically shows correct provider name<\/li>\n<li>New: History shortcode <code>year<\/code> parameter (<code>[naws_history year=\"2025\"]<\/code>)<\/li>\n<li>Improved: Appearance admin page streamlined, unused sections removed<\/li>\n<\/ul>\n\n<h4>1.4.1<\/h4>\n\n<ul>\n<li>Fix: 24h chart gradient fill restored (hex-to-RGBA conversion with canvas gradient)<\/li>\n<li>Improved: Appearance page redesigned with live previews for all color groups<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<ul>\n<li>New: Appearance page with 130+ configurable colors and WordPress Color Picker<\/li>\n<li>New: <code>NAWS_Colors<\/code> class with centralized color management and caching<\/li>\n<li>New: Theme colors, accent colors, sensor tile gradients, chart theming, year palette<\/li>\n<li>New: Reset-to-defaults functionality<\/li>\n<li>New: 60+ translation strings for color settings<\/li>\n<li>Improved: All frontend colors use CSS custom properties<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>New: Export \/ Import feature with full backup and restore<\/li>\n<li>New: Weather data export as JSON, full backup export (data + modules + settings)<\/li>\n<li>New: File import with chunked AJAX processing and real-time progress<\/li>\n<li>Security: API tokens are never included in exports<\/li>\n<li>New: <code>NAWS_Export<\/code> class with streaming export for large datasets<\/li>\n<\/ul>\n\n<h4>1.2.1<\/h4>\n\n<ul>\n<li>Fix: All <code>json_encode()<\/code> replaced with <code>wp_json_encode()<\/code> (Plugin Check compliance)<\/li>\n<li>Fix: SQL injection hardening with <code>$wpdb-&gt;prepare()<\/code> for DELETE queries<\/li>\n<li>Fix: <code>TRUNCATE<\/code> replaced with <code>DELETE FROM<\/code> for WordPress compatibility<\/li>\n<li>Fix: Deprecated <code>date_i18n()<\/code> replaced with <code>wp_date()<\/code><\/li>\n<li>Fix: Debug endpoint sanitized (truncated responses, stripped tokens)<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>New: Mobile-first responsive redesign with standardized breakpoints (480\/600\/768\/1024px)<\/li>\n<li>New: Touch-friendly targets meeting WCAG 44x44px minimum<\/li>\n<li>New: Responsive wind compass with <code>clamp()<\/code> sizing<\/li>\n<li>New: Dynamic chart font sizing based on viewport<\/li>\n<li>Improved: ~400 lines of inline styles extracted to centralized <code>frontend.css<\/code><\/li>\n<li>Improved: ID selectors replaced with reusable class selectors<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>New: Central error logging (<code>NAWS_Logger<\/code>) with severity levels and sensitive data redaction<\/li>\n<li>New: Transient caching layer for database queries (modules, readings, daily summaries)<\/li>\n<li>New: Adaptive polling with error backoff (doubles interval after 3 failures)<\/li>\n<li>New: Night mode with reduced polling between 23:00\u201306:00<\/li>\n<li>New: Health status indicator in admin dashboard (green\/yellow\/red)<\/li>\n<li>New: Frontend error UI and AJAX retry logic with exponential backoff<\/li>\n<li>New: <code>naws_data_synced<\/code> action hook for extensibility<\/li>\n<li>Fix: N+1 query in history data replaced with single query<\/li>\n<li>Fix: Silent DB errors now logged<\/li>\n<li>Fix: Chart.js blank page prevented with try\/catch<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Removed shortcodes: <code>[naws_chart]<\/code>, <code>[naws_gauge]<\/code>, <code>[naws_dashboard]<\/code>, <code>[naws_card]<\/code> \u2013 use <code>[naws_live]<\/code> and <code>[naws_history]<\/code> instead<\/li>\n<li>Removed: gauge.min.js vendor library and unused templates<\/li>\n<li>Fix: Fatal error on activation (<code>spawn_cron()<\/code> called too early)<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>New: Forecast provider selection (Open-Meteo + Yr.no \/ MET Norway)<\/li>\n<li>New: Norwegian (Bokmal) language with 326 translated keys<\/li>\n<li>New: File-based language system (one PHP file per language, auto-discovered)<\/li>\n<li>New: Configurable station name in Settings<\/li>\n<li>Fix: OAuth flow broken by encryption (auto-migration)<\/li>\n<li>Fix: OAuth state validation with <code>hash_equals()<\/code> and 10-min expiry<\/li>\n<li>Fix: Cron stops after plugin update (activation hook + watchdog fix)<\/li>\n<li>Fix: SVG weather icons stripped by <code>wp_kses_post()<\/code><\/li>\n<li>Improved: Plugin Check compliance (126 errors to 0)<\/li>\n<li>Improved: Vendor JS files bundled in ZIP<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial public release<\/li>\n<li>Full Netatmo OAuth2 integration with all module types<\/li>\n<li>Live dashboard with animated sensor cards and 24h charts<\/li>\n<li>Astronomy: sunrise\/sunset, moon phase, next full moon<\/li>\n<li>Derived weather: feels-like temperature, heat index, dew point<\/li>\n<li>Year-over-year history charts (temperature, pressure, monthly rainfall)<\/li>\n<li>5-day weather forecast via Open-Meteo<\/li>\n<li>REST API with API key authentication and rate limiting<\/li>\n<li>AES-256-GCM encryption for all stored credentials<\/li>\n<li>Full German and English localization<\/li>\n<li>Configurable units (temperature, rain, wind, pressure)<\/li>\n<li>Cron watchdog with self-healing for stuck sync jobs<\/li>\n<li>Historical data importer with batch processing<\/li>\n<\/ul>","raw_excerpt":"Connects to the Netatmo API, stores all sensor data locally and displays live dashboards, animated charts, history and weather forecasts.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/294194","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=294194"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/xylaender"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=294194"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=294194"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=294194"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=294194"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=294194"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=294194"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}