2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.homematic.internal.type.generator;
15 import java.io.BufferedReader;
16 import java.io.BufferedWriter;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.OutputStreamWriter;
23 import java.util.Date;
25 import java.util.Map.Entry;
26 import java.util.TreeMap;
29 * The CcuMetadataExtractor loads some JavaScript files from the CCU and generates the device and datapoint
30 * descriptions into the file generated-descriptions.properties.
31 * Not all descriptions can be generated, because they are spread across many files and were partly assembled.
32 * To add the missing descriptions or to override a generated, use the extra-descriptions.properties file.
34 * @author Gerhard Riegler - Initial contribution
36 public class CcuMetadataExtractor {
37 private static final String CCU_URL = "http://ccu";
39 private static final String DEVICE_KEYS = "/webui/webui.js";
40 private static final String DESCRIPTION_KEYS = "/webui/js/lang/{LANGUAGE}/translate.lang.stringtable.js";
41 private static final String DESCRIPTION_DESCRIPTIONS = "/webui/js/lang/{LANGUAGE}/translate.lang.deviceDescription.js";
43 private static final String MASTER_LANG_PATH = "/config/easymodes/MASTER_LANG/";
44 private static final String[] MASTER_LANG_FILES = { "HEATINGTHERMOSTATE_2ND_GEN.js", "HM_ES_PMSw.js" };
46 private static final String[] LANGUAGES = { "en", "de" };
49 * Starts the extractor and generates the file.
51 public static void main(String[] args) {
53 CcuMetadataExtractor dg = new CcuMetadataExtractor();
56 } catch (IOException ex) {
57 System.err.println(ex.getMessage());
62 * Loads the JavaScript file from the CCU and generates the properties file.
64 private void generate() throws IOException {
65 Map<String, String> deviceKeys = loadDeviceKeys();
67 for (String lang : LANGUAGES) {
68 // load datapoint descriptions
69 Map<String, String> langDescriptions = loadJsonLangDescriptionFile(CCU_URL + DESCRIPTION_KEYS, lang);
70 for (String fileName : MASTER_LANG_FILES) {
71 langDescriptions.putAll(loadJsonLangDescriptionFile(CCU_URL + MASTER_LANG_PATH + fileName, lang));
74 // load device descriptions
75 Map<String, String> deviceDescriptions = loadJsonLangDescriptionFile(CCU_URL + DESCRIPTION_DESCRIPTIONS,
78 String langIdent = ("en".equals(lang) ? "" : "_" + lang);
79 File file = new File("./src/main/resources/homematic/generated-descriptions" + langIdent + ".properties");
80 System.out.println("Writing file " + file.getAbsolutePath());
85 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
87 // write device descriptions
88 bw.write("# -------------- generated descriptions " + new Date() + " --------------\n");
89 bw.write("# DON'T CHANGE THIS FILE!\n\n");
90 for (Entry<String, String> entry : deviceDescriptions.entrySet()) {
91 bw.write(entry.getKey());
93 bw.write(entry.getValue());
99 // write datapoint descriptions
100 for (Entry<String, String> entry : deviceKeys.entrySet()) {
101 String description = langDescriptions.get(entry.getValue());
102 if (description == null) {
103 System.out.println("WARNING: Can't find a translation for " + entry.getValue());
105 bw.write(entry.getKey().toUpperCase());
107 bw.write(description);
118 * Loads a JavaScript file and extract JSON description data.
120 private Map<String, String> loadJsonLangDescriptionFile(String url, String lang) throws IOException {
121 final Map<String, String> descriptions = new TreeMap<>();
122 String descriptionUrl = url.replace("{LANGUAGE}", lang);
124 String startLine = " \"" + lang + "\" : {";
125 String endLine = " },";
127 new UrlLoader(descriptionUrl, startLine, endLine) {
129 public void line(String line) {
130 String[] entry = handleStringTable(line);
132 descriptions.put(entry[0].trim(), unescape(entry[1].trim()));
140 * Loads all description keys.
142 private Map<String, String> loadDeviceKeys() throws IOException {
143 final Map<String, String> deviceKeys = new TreeMap<>();
145 new UrlLoader(CCU_URL + DEVICE_KEYS) {
148 public void line(String line) {
149 if (line.startsWith("elvST['")) {
150 line = line.replace("elvST['", "");
151 line = line.replace("'] = '", "=");
152 line = line.replace("';", "");
153 line = line.replace("$", "");
154 line = line.replace("{", "");
155 line = line.replace("}", "");
158 for (int i = 0; i < line.length(); i++) {
159 if (line.charAt(i) == '=') {
164 line = line.replaceFirst("=", "|");
166 String[] split = line.split("=", 2);
167 deviceKeys.put(split[0].trim(), split[1].trim());
175 * Splits a JSON JavaScript entry.
177 private String[] handleStringTable(String line) {
178 line = line.replace(" \"", "");
179 line = line.replace("\",", "");
180 line = line.replace("\"", "");
182 String[] splitted = line.split(":", 2);
183 return splitted.length != 2 ? null : splitted;
187 * Transforms a string for a Java property file.
189 private String unescape(String str) {
190 str = str.replace("%FC", "ü");
191 str = str.replace("%DC", "Ü");
192 str = str.replace("%E4", "ä");
193 str = str.replace("%C4", "Ä");
194 str = str.replace("%F6", "ö");
195 str = str.replace("%D6", "Ö");
196 str = str.replace("%DF", "ß");
197 str = str.replace(" ", "");
198 str = str.replace("<br/>", " ");
199 str = str.replace("ü", "ü");
200 str = str.replace("ä", "ä");
201 str = str.replace("ö", "ö");
202 str = str.replace("Ü", "Ü");
203 str = str.replace("Ä", "Ä");
204 str = str.replace("Ö", "Ö");
209 * Simple class to load a CCU resource.
211 private abstract class UrlLoader {
212 public UrlLoader(String url) throws IOException {
213 this(url, null, null);
216 public UrlLoader(String url, String startLine, String endLine) throws IOException {
217 System.out.println("Loading file " + url);
218 Boolean includeLine = null;
219 BufferedReader br = new BufferedReader(new InputStreamReader(new URL(url).openStream(), "UTF-8"));
222 while ((line = br.readLine()) != null) {
223 if (startLine != null) {
224 if (line.equals(startLine)) {
227 } else if (line.equals(endLine) || includeLine == null) {
231 if ((includeLine == null || includeLine) && !line.isBlank()) {
238 public abstract void line(String line);