2 * Copyright (c) 2010-2020 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;
28 import org.apache.commons.lang.StringEscapeUtils;
29 import org.apache.commons.lang.StringUtils;
32 * The CcuMetadataExtractor loads some JavaScript files from the CCU and generates the device and datapoint
33 * descriptions into the file generated-descriptions.properties.
34 * Not all descriptions can be generated, because they are spread across many files and were partly assembled.
35 * To add the missing descriptions or to override a generated, use the extra-descriptions.properties file.
37 * @author Gerhard Riegler - Initial contribution
39 public class CcuMetadataExtractor {
40 private static final String CCU_URL = "http://ccu";
42 private static final String DEVICE_KEYS = "/webui/webui.js";
43 private static final String DESCRIPTION_KEYS = "/webui/js/lang/{LANGUAGE}/translate.lang.stringtable.js";
44 private static final String DESCRIPTION_DESCRIPTIONS = "/webui/js/lang/{LANGUAGE}/translate.lang.deviceDescription.js";
46 private static final String MASTER_LANG_PATH = "/config/easymodes/MASTER_LANG/";
47 private static final String[] MASTER_LANG_FILES = { "HEATINGTHERMOSTATE_2ND_GEN.js", "HM_ES_PMSw.js" };
49 private static final String[] LANGUAGES = { "en", "de" };
52 * Starts the extractor and generates the file.
54 public static void main(String[] args) {
56 CcuMetadataExtractor dg = new CcuMetadataExtractor();
59 } catch (IOException ex) {
60 System.err.println(ex.getMessage());
65 * Loads the JavaScript file from the CCU and generates the properties file.
67 private void generate() throws IOException {
68 Map<String, String> deviceKeys = loadDeviceKeys();
70 for (String lang : LANGUAGES) {
71 // load datapoint descriptions
72 Map<String, String> langDescriptions = loadJsonLangDescriptionFile(CCU_URL + DESCRIPTION_KEYS, lang);
73 for (String fileName : MASTER_LANG_FILES) {
74 langDescriptions.putAll(loadJsonLangDescriptionFile(CCU_URL + MASTER_LANG_PATH + fileName, lang));
77 // load device descriptions
78 Map<String, String> deviceDescriptions = loadJsonLangDescriptionFile(CCU_URL + DESCRIPTION_DESCRIPTIONS,
81 String langIdent = ("en".equals(lang) ? "" : "_" + lang);
82 File file = new File("./src/main/resources/homematic/generated-descriptions" + langIdent + ".properties");
83 System.out.println("Writing file " + file.getAbsolutePath());
88 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "ISO-8859-1"));
90 // write device descriptions
91 bw.write("# -------------- generated descriptions " + new Date() + " --------------\n");
92 bw.write("# DON'T CHANGE THIS FILE!\n\n");
93 for (Entry<String, String> entry : deviceDescriptions.entrySet()) {
94 bw.write(entry.getKey());
96 bw.write(entry.getValue());
102 // write datapoint descriptions
103 for (Entry<String, String> entry : deviceKeys.entrySet()) {
104 String description = langDescriptions.get(entry.getValue());
105 if (description == null) {
106 System.out.println("WARNING: Can't find a translation for " + entry.getValue());
108 bw.write(entry.getKey().toUpperCase());
110 bw.write(description);
121 * Loads a JavaScript file and extract JSON description data.
123 private Map<String, String> loadJsonLangDescriptionFile(String url, String lang) throws IOException {
124 final Map<String, String> descriptions = new TreeMap<>();
125 String descriptionUrl = StringUtils.replace(url, "{LANGUAGE}", lang);
127 String startLine = " \"" + lang + "\" : {";
128 String endLine = " },";
130 new UrlLoader(descriptionUrl, startLine, endLine) {
132 public void line(String line) {
133 String[] entry = handleStringTable(line);
135 descriptions.put(StringUtils.trim(entry[0]), unescape(StringUtils.trim(entry[1])));
143 * Loads all description keys.
145 private Map<String, String> loadDeviceKeys() throws IOException {
146 final Map<String, String> deviceKeys = new TreeMap<>();
148 new UrlLoader(CCU_URL + DEVICE_KEYS) {
151 public void line(String line) {
152 if (line.startsWith("elvST['")) {
153 line = StringUtils.remove(line, "elvST['");
154 line = StringUtils.replace(line, "'] = '", "=");
155 line = StringUtils.remove(line, "';");
156 line = StringUtils.remove(line, "$");
157 line = StringUtils.remove(line, "{");
158 line = StringUtils.remove(line, "}");
160 int count = StringUtils.countMatches(line, "=");
162 line = StringUtils.replace(line, "=", "|", 1);
165 String[] split = StringUtils.split(line, "=", 2);
166 deviceKeys.put(StringUtils.trim(split[0]), StringUtils.trim(split[1]));
174 * Splits a JSON JavaScript entry.
176 private String[] handleStringTable(String line) {
177 line = StringUtils.remove(line, " \"");
178 line = StringUtils.remove(line, "\",");
179 line = StringUtils.remove(line, "\"");
181 String[] splitted = StringUtils.split(line, ":", 2);
182 return splitted.length != 2 ? null : splitted;
186 * Transforms a string for a Java property file.
188 private String unescape(String str) {
189 str = StringUtils.replace(str, "%FC", "ü");
190 str = StringUtils.replace(str, "%DC", "Ü");
191 str = StringUtils.replace(str, "%E4", "ä");
192 str = StringUtils.replace(str, "%C4", "Ä");
193 str = StringUtils.replace(str, "%F6", "ö");
194 str = StringUtils.replace(str, "%D6", "Ö");
195 str = StringUtils.replace(str, "%DF", "ß");
196 str = StringUtils.remove(str, " ");
197 str = StringUtils.replace(str, "<br/>", " ");
198 str = StringEscapeUtils.unescapeHtml(str);
203 * Simple class to load a CCU resource.
205 private abstract class UrlLoader {
206 public UrlLoader(String url) throws IOException {
207 this(url, null, null);
210 public UrlLoader(String url, String startLine, String endLine) throws IOException {
211 System.out.println("Loading file " + url);
212 Boolean includeLine = null;
213 BufferedReader br = new BufferedReader(new InputStreamReader(new URL(url).openStream(), "ISO-8859-1"));
216 while ((line = br.readLine()) != null) {
217 if (startLine != null) {
218 if (line.equals(startLine)) {
221 } else if (line.equals(endLine) || includeLine == null) {
225 if ((includeLine == null || includeLine) && StringUtils.isNotBlank(line)) {
232 public abstract void line(String line);