<commit_before>package org.juurlink.atagone;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.Nonnull;

import org.juurlink.atagone.domain.Configuration;
import org.juurlink.atagone.exceptions.AtagPageErrorException;
import org.juurlink.atagone.utils.HTMLUtils;
import org.juurlink.atagone.utils.HTTPUtils;
import org.juurlink.atagone.utils.JSONUtils;
import org.juurlink.atagone.utils.NumberUtils;
import org.juurlink.atagone.utils.StringUtils;

import lombok.NonNull;
import lombok.extern.java.Log;

/**
 * Connect to ATAG One thermostat through ATAG One (internet) portal.
 */
@Log
public class AtagOneRemoteConnector implements AtagOneConnectorInterface {

	private static final String THERMOSTAT_NAME = "ATAG One";

	private static final String URL_LOGIN = "https://portal.atag-one.com/Account/Login";
	private static final String URL_DEVICE_HOME = "https://portal.atag-one.com/Home/Index/{0}";
	private static final String URL_DIAGNOSTICS = "https://portal.atag-one.com/Device/LatestReport";
	private static final String URL_UPDATE_DEVICE_CONTROL = "https://portal.atag-one.com/Home/UpdateDeviceControl/?deviceId={0}";
	private static final String URL_DEVICE_SET_SETPOINT = "https://portal.atag-one.com/Home/DeviceSetSetpoint";

	private Configuration configuration;

	/**
	 * ATAG One device ID.
	 */
	@NonNull
	private String selectedDeviceId;

	/**
	 * Construct ATAG One connector to remote device.
	 */
	public AtagOneRemoteConnector(@Nonnull @NonNull final Configuration configuration) {
		log.fine("Instantiate " + AtagOneApp.THERMOSTAT_NAME + " remote connector");
		this.configuration = configuration;
	}

	/**
	 * Login ATAG ONE portal and select first Device found.
	 *
	 * @return Device ID
	 */
	@Nonnull
	public String login() throws IOException, AtagPageErrorException {

		log.fine("Login at " + THERMOSTAT_NAME + " portal.");
		log.fine("POST authentication data: " + URL_LOGIN);

		// We need a session (cookie) and a verification token, get them first.
		String requestVerificationToken = getRequestVerificationToken(URL_LOGIN);

		Map<String, String> params = new LinkedHashMap<String, String>();
		params.put("__RequestVerificationToken", requestVerificationToken);
		params.put("Email", configuration.getEmail());
		params.put("Password", <PASSWORD>());
		params.put("RememberMe", "false");

		String html = HTTPUtils.getPostPageContent(URL_LOGIN, params);
		selectedDeviceId = HTMLUtils.extractDeviceIdFromHtml(html);

		if (StringUtils.isBlank(selectedDeviceId)) {
			throw new IllegalStateException("No Device ID found, cannot continue.");
		}

		return selectedDeviceId;
	}

	/**
	 * Get all diagnostics for selected device.
	 *
	 * @return Map of diagnostic info
	 * @throws IOException              in case of connection error
	 * @throws IllegalArgumentException when no device selected
	 */
	@Nonnull
	public Map<String, Object> getDiagnostics() throws IOException, IllegalArgumentException, AtagPageErrorException {

		if (StringUtils.isBlank(selectedDeviceId)) {
			throw new IllegalArgumentException("No Device selected, cannot get diagnostics.");
		}

		final String diagnosticsUrl = URL_DIAGNOSTICS + "?deviceId=" + URLEncoder.encode(selectedDeviceId, ENCODING_UTF_8);
		log.fine("GET diagnostics: URL=" + diagnosticsUrl);

		// HTTP(S) Connect.
		String html = HTTPUtils.getPageContent(diagnosticsUrl);
		log.fine("GET diagnostics: Response HTML\n" + html);

		// Scrape values from HTML page.
		Map<String, Object> values = new LinkedHashMap<String, Object>();
		values.put(VALUE_DEVICE_ID, selectedDeviceId);
		values.put(VALUE_DEVICE_ALIAS, HTMLUtils.getValueByLabel(html, String.class, "Apparaat alias", "Device alias"));
		values.put(VALUE_LATEST_REPORT_TIME, HTMLUtils.getValueByLabel(html, String.class, "Laatste rapportagetijd", "Latest report time"));
		values.put(VALUE_CONNECTED_TO, HTMLUtils.getValueByLabel(html, String.class, "Verbonden met", "Connected to"));
		values.put(VALUE_BURNING_HOURS, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Branduren", "Burning hours"));
		values.put(VALUE_BOILER_HEATING_FOR, HTMLUtils.getValueByLabel(html, String.class, "Ketel in bedrijf voor", "Boiler heating for"));
		values.put(VALUE_FLAME_STATUS, HTMLUtils.getValueByLabel(html, Boolean.class, "Brander status", "Flame status"));
		values.put(VALUE_ROOM_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Kamertemperatuur", "Room temperature"));
		values.put(VALUE_OUTSIDE_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Buitentemperatuur", "Outside temperature"));
		values.put(VALUE_DHW_SETPOINT, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Setpoint warmwater", "DHW setpoint"));
		values.put(VALUE_DHW_WATER_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Warmwatertemperatuur", "DHW water temperature"));
		values.put(VALUE_CH_SETPOINT, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Setpoint cv", "CH setpoint"));
		values.put(VALUE_CH_WATER_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "CV-aanvoertemperatuur", "CH water temperature"));
		values.put(VALUE_CH_WATER_PRESSURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "CV-waterdruk", "CH water pressure"));
		values.put(VALUE_CH_RETURN_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "CV retourtemperatuur", "CH return temperature"));

		// We have to do an extra call to get the target temperature.
		// {"isHeating":false,"targetTemp":"17.0","currentTemp":"16.9","vacationPlanned":false,"currentMode":"manual"}
		String deviceControlUrl = URL_UPDATE_DEVICE_CONTROL.replace("{0}", URLEncoder.encode(selectedDeviceId, ENCODING_UTF_8));
		log.fine("GET deviceControl: URL=" + deviceControlUrl);

		// HTTP(S) Connect.
		html = HTTPUtils.getPageContent(deviceControlUrl);
		log.fine("GET deviceControl: Response HTML\n" + html);

		final String targetTemp = JSONUtils.getJSONValueByName(html, String.class, "targetTemp");
		final BigDecimal targetTempNumber = new BigDecimal(targetTemp != null ? targetTemp : "0");
		values.put(VALUE_TARGET_TEMPERATURE, targetTempNumber);
		values.put(VALUE_CURRENT_MODE, JSONUtils.getJSONValueByName(html, String.class, "currentMode"));
		values.put(VALUE_VACATION_PLANNED, JSONUtils.getJSONValueByName(html, String.class, "vacationPlanned"));

		return values;
	}

	/**
	 * Set thermostat target temperature.
	 *
	 * @return current room temperature or null when temperature not found in response
	 */<commit_msg>Fixed vacationPlanned parsing<commit_after>package org.juurlink.atagone;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.Nonnull;

import org.juurlink.atagone.domain.Configuration;
import org.juurlink.atagone.exceptions.AtagPageErrorException;
import org.juurlink.atagone.utils.HTMLUtils;
import org.juurlink.atagone.utils.HTTPUtils;
import org.juurlink.atagone.utils.JSONUtils;
import org.juurlink.atagone.utils.NumberUtils;
import org.juurlink.atagone.utils.StringUtils;

import lombok.NonNull;
import lombok.extern.java.Log;

/**
 * Connect to ATAG One thermostat through ATAG One (internet) portal.
 */
@Log
public class AtagOneRemoteConnector implements AtagOneConnectorInterface {

	private static final String THERMOSTAT_NAME = "ATAG One";

	private static final String URL_LOGIN = "https://portal.atag-one.com/Account/Login";
	private static final String URL_DEVICE_HOME = "https://portal.atag-one.com/Home/Index/{0}";
	private static final String URL_DIAGNOSTICS = "https://portal.atag-one.com/Device/LatestReport";
	private static final String URL_UPDATE_DEVICE_CONTROL = "https://portal.atag-one.com/Home/UpdateDeviceControl/?deviceId={0}";
	private static final String URL_DEVICE_SET_SETPOINT = "https://portal.atag-one.com/Home/DeviceSetSetpoint";

	private Configuration configuration;

	/**
	 * ATAG One device ID.
	 */
	@NonNull
	private String selectedDeviceId;

	/**
	 * Construct ATAG One connector to remote device.
	 */
	public AtagOneRemoteConnector(@Nonnull @NonNull final Configuration configuration) {
		log.fine("Instantiate " + AtagOneApp.THERMOSTAT_NAME + " remote connector");
		this.configuration = configuration;
	}

	/**
	 * Login ATAG ONE portal and select first Device found.
	 *
	 * @return Device ID
	 */
	@Nonnull
	public String login() throws IOException, AtagPageErrorException {

		log.fine("Login at " + THERMOSTAT_NAME + " portal.");
		log.fine("POST authentication data: " + URL_LOGIN);

		// We need a session (cookie) and a verification token, get them first.
		String requestVerificationToken = getRequestVerificationToken(URL_LOGIN);

		Map<String, String> params = new LinkedHashMap<String, String>();
		params.put("__RequestVerificationToken", requestVerificationToken);
		params.put("Email", configuration.getEmail());
		params.put("Password", configuration.<PASSWORD>());
		params.put("RememberMe", "false");

		String html = HTTPUtils.getPostPageContent(URL_LOGIN, params);
		selectedDeviceId = HTMLUtils.extractDeviceIdFromHtml(html);

		if (StringUtils.isBlank(selectedDeviceId)) {
			throw new IllegalStateException("No Device ID found, cannot continue.");
		}

		return selectedDeviceId;
	}

	/**
	 * Get all diagnostics for selected device.
	 *
	 * @return Map of diagnostic info
	 * @throws IOException              in case of connection error
	 * @throws IllegalArgumentException when no device selected
	 */
	@Nonnull
	public Map<String, Object> getDiagnostics() throws IOException, IllegalArgumentException, AtagPageErrorException {

		if (StringUtils.isBlank(selectedDeviceId)) {
			throw new IllegalArgumentException("No Device selected, cannot get diagnostics.");
		}

		final String diagnosticsUrl = URL_DIAGNOSTICS + "?deviceId=" + URLEncoder.encode(selectedDeviceId, ENCODING_UTF_8);
		log.fine("GET diagnostics: URL=" + diagnosticsUrl);

		// HTTP(S) Connect.
		String html = HTTPUtils.getPageContent(diagnosticsUrl);
		log.fine("GET diagnostics: Response HTML\n" + html);

		// Scrape values from HTML page.
		Map<String, Object> values = new LinkedHashMap<String, Object>();
		values.put(VALUE_DEVICE_ID, selectedDeviceId);
		values.put(VALUE_DEVICE_ALIAS, HTMLUtils.getValueByLabel(html, String.class, "Apparaat alias", "Device alias"));
		values.put(VALUE_LATEST_REPORT_TIME, HTMLUtils.getValueByLabel(html, String.class, "Laatste rapportagetijd", "Latest report time"));
		values.put(VALUE_CONNECTED_TO, HTMLUtils.getValueByLabel(html, String.class, "Verbonden met", "Connected to"));
		values.put(VALUE_BURNING_HOURS, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Branduren", "Burning hours"));
		values.put(VALUE_BOILER_HEATING_FOR, HTMLUtils.getValueByLabel(html, String.class, "Ketel in bedrijf voor", "Boiler heating for"));
		values.put(VALUE_FLAME_STATUS, HTMLUtils.getValueByLabel(html, Boolean.class, "Brander status", "Flame status"));
		values.put(VALUE_ROOM_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Kamertemperatuur", "Room temperature"));
		values.put(VALUE_OUTSIDE_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Buitentemperatuur", "Outside temperature"));
		values.put(VALUE_DHW_SETPOINT, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Setpoint warmwater", "DHW setpoint"));
		values.put(VALUE_DHW_WATER_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Warmwatertemperatuur", "DHW water temperature"));
		values.put(VALUE_CH_SETPOINT, HTMLUtils.getValueByLabel(html, BigDecimal.class, "Setpoint cv", "CH setpoint"));
		values.put(VALUE_CH_WATER_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "CV-aanvoertemperatuur", "CH water temperature"));
		values.put(VALUE_CH_WATER_PRESSURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "CV-waterdruk", "CH water pressure"));
		values.put(VALUE_CH_RETURN_TEMPERATURE, HTMLUtils.getValueByLabel(html, BigDecimal.class, "CV retourtemperatuur", "CH return temperature"));

		// We have to do an extra call to get the target temperature.
		// {"isHeating":false,"targetTemp":"17.0","currentTemp":"16.9","vacationPlanned":false,"currentMode":"manual"}
		String deviceControlUrl = URL_UPDATE_DEVICE_CONTROL.replace("{0}", URLEncoder.encode(selectedDeviceId, ENCODING_UTF_8));
		log.fine("GET deviceControl: URL=" + deviceControlUrl);

		// HTTP(S) Connect.
		html = HTTPUtils.getPageContent(deviceControlUrl);
		log.fine("GET deviceControl: Response HTML\n" + html);

		final String targetTemp = JSONUtils.getJSONValueByName(html, String.class, "targetTemp");
		final BigDecimal targetTempNumber = new BigDecimal(targetTemp != null ? targetTemp : "0");
		values.put(VALUE_TARGET_TEMPERATURE, targetTempNumber);
		values.put(VALUE_CURRENT_MODE, JSONUtils.getJSONValueByName(html, String.class, "currentMode"));
		values.put(VALUE_VACATION_PLANNED, JSONUtils.getJSONValueByName(html, Boolean.class, "vacationPlanned"));

		return values;
	}

	/**
	 * Set thermostat target temperature.
	 *
	 * @return current room temperature or null when temperature not found in response
	 */
{'fraction_non_alphanumeric': 0.11596943793025191, 'fraction_numerical': 0.001815568499886527, 'mean_word_length': 6.046908315565032, 'pattern_counts': {'":': 10, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4152824', 'n_tokens_mistral': 4229, 'n_tokens_neox': 4027, 'n_words': 888}