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
14 package org.openhab.binding.miio.internal;
17 import java.security.MessageDigest;
18 import java.security.NoSuchAlgorithmException;
19 import java.util.LinkedHashMap;
20 import java.util.LinkedHashSet;
21 import java.util.Map.Entry;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jetty.client.HttpClient;
25 import org.eclipse.jetty.util.ssl.SslContextFactory;
26 import org.junit.jupiter.api.Disabled;
27 import org.openhab.binding.miio.internal.basic.MiIoBasicDevice;
28 import org.openhab.binding.miio.internal.miot.MiotParseException;
29 import org.openhab.binding.miio.internal.miot.MiotParser;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import com.google.gson.Gson;
34 import com.google.gson.GsonBuilder;
37 * Supporting tool for creation of the json database files for miot devices
39 * Run in IDE with 'run as java application' or run in command line as:
40 * mvn exec:java -Dexec.mainClass="org.openhab.binding.miio.internal.MiotJsonFileCreator" -Dexec.classpathScope="test"
41 * -Dexec.args="zhimi.humidifier.ca4"
43 * The argument is the model string to create the database file for.
44 * If the digit at the end of the model is omitted, it will try a range of devices
46 * @author Marcel Verpaalen - Initial contribution
50 public class MiotJsonFileCreator {
51 private static final Logger LOGGER = LoggerFactory.getLogger(MiotJsonFileCreator.class);
52 private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
54 private static final String BASEDIR = "./src/main/resources/database/";
55 private static final String FILENAME_EXTENSION = "-miot.json";
56 private static final boolean OVERWRITE_EXISTING_DATABASE_FILE = false;
59 public static void main(String[] args) {
60 LinkedHashMap<String, String> checksums = new LinkedHashMap<>();
61 LinkedHashSet<String> models = new LinkedHashSet<>();
62 if (args.length > 0) {
66 String m = models.isEmpty() ? "" : (String) models.toArray()[0];
67 boolean scan = m.isEmpty() ? false : !Character.isDigit(m.charAt(m.length() - 1));
69 for (int i = 1; i <= 12; i++) {
70 models.add(models.toArray()[0] + String.valueOf(i));
74 MiotParser miotParser;
75 for (String model : models) {
76 LOGGER.info("Processing: {}", model);
77 HttpClient httpClient = null;
79 httpClient = new HttpClient(new SslContextFactory.Client());
80 httpClient.setFollowRedirects(false);
82 miotParser = MiotParser.parse(model, httpClient);
83 LOGGER.info("urn: ", miotParser.getUrn());
84 LOGGER.info("{}", miotParser.getUrnData());
85 MiIoBasicDevice device = miotParser.getDevice();
87 LOGGER.info("Device: {}", device);
88 String fileName = String.format("%s%s%s", BASEDIR, model, FILENAME_EXTENSION);
89 if (!OVERWRITE_EXISTING_DATABASE_FILE) {
91 while (new File(fileName).isFile()) {
92 fileName = String.format("%s%s-%d%s", BASEDIR, model, counter, FILENAME_EXTENSION);
96 miotParser.writeDevice(fileName, device);
97 String channelsJson = GSON.toJson(device.getDevice().getChannels()).toString();
98 checksums.put(model, checksumMD5(channelsJson));
100 LOGGER.info("Finished");
101 } catch (MiotParseException e) {
102 LOGGER.info("Error processing model {}: {}", model, e.getMessage());
103 } catch (Exception e) {
104 LOGGER.info("Failed to initiate http Client: {}", e.getMessage());
107 if (httpClient != null && httpClient.isRunning()) {
110 } catch (Exception e) {
115 StringBuilder sb = new StringBuilder();
116 for (Entry<String, String> ch : checksums.entrySet()) {
117 sb.append(ch.getValue());
119 sb.append(ch.getKey());
122 LOGGER.info("Checksums for device comparisons\r\n{}", sb);
125 public static String checksumMD5(String input) {
127 MessageDigest md = MessageDigest.getInstance("MD5");
128 md.update(input.getBytes());
129 return Utils.getHex(md.digest());
130 } catch (NoSuchAlgorithmException e) {