Lenovo Smart Clock 2: unboxing and exploration

If you’ve seen YouTube unboxing videos, this is not that. There is no video here (although I think unboxing videos are great and more people should do them of more devices!) and what you’ll find described below has little to do with direct user experience.

The Lenovo Smart Clock 2 looks like a fun little device. It is sold as, well, a Smart Clock, but a quick web search clarifies what it is: a MT8167S Android 10 device with WiFi, BlueTooth, a 4 inch IPS 800x400 touch screen, 1 gigabyte of RAM, 8 gigabyte of flash, and apparently a pretty decent speaker. I managed to snag one at 30 EUR, which seems like a steal for such a device. Specification wise, it appears to be roughly on par with a Raspberry Pi 3 — plus, again, WiFi, BT, storage, and a screen.

As sold, a Google “Smart Display”, it already seems fun. If it had line out for audio, it might also be the perfect music player for me.

However, that is not my primary interest. Can we make this a nice Home Assistant controller? A cheap Streamdeck equivalent? Or, given its specifications, a small home server (for Home Assistant or some other service) that also happens to be a nice looking screen?

I found two interesting threads on the XDA Forums:

I especially enjoyed that second thread. The guide, as it currently stands there, is good (and functional) but some of the steps seemed entirely alien to me. In a future post, I will take the method described in it, explain the parts from which that method is composed, and also offer a few new tricks.

But, that post is not this post. In this post, I will unpack the clock and dig around in other ways.

Unpacking and first explorations

The inside of the box greets us with a very sleek looking device with a body covered in a nice fabric. (Also, a power adapter and a booklet).

On startup, we are greeted with a very short setup hint.

First startup screen

Note that it says “Nest” !

My impression is that Google has tied a bunch of device types under a Nest or Nest-like moniker - all the page tells you to do is open the Google Home app anyway. (Honestly, after starting the Google Home app, I was somewhat lost. The next steps to take could have been outlined more clearly.)

Now, I did not immediately proceed to follow these instructions. Clearly the device is, itself, waiting for instructions. Maybe we can give it some?

On the screen, we see the current Build version (1.52.257161) and what appears to be some unique device ID (bottom right corner, says LenovoCD-24502F1428). At this point, it exposes itself as a Wifi access point with SSID LenovoCD-24502F1428.q003. Connecting to it yields us an IP in the 192.168.43.0/24 range, with default gateway also set to an IP in the range. Between reboots, the IP it picks in this range appears to vary.

Here’s some nmap output:

Starting Nmap 7.80 ( https://nmap.org ) at 2022-11-26 23:26 CET
Nmap scan report for 192.168.43.166 (192.168.43.166)
Host is up (0.084s latency).
Not shown: 994 closed ports
PORT      STATE SERVICE
53/tcp    open  domain
8008/tcp  open  http
8009/tcp  open  ajp13
8443/tcp  open  https-alt
9000/tcp  open  cslistener
10001/tcp open  scp-config
MAC Address: 32:86:AC:C2:02:D6 (Unknown)

Some quick investigation followed. (Unfold to read.)

Port 8008:
$ curl -v 192.168.43.166:8008
*   Trying 192.168.43.166:8008...
* Connected to 192.168.43.166 (192.168.43.166) port 8008 (#0)
> GET / HTTP/1.1
> Host: 192.168.43.166:8008
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Content-Length:0
< Content-Type:text/html
< 
* Connection #0 to host 192.168.43.166 left intact

Port 8009 closed our connection when we tried to speak HTTP. No TLS handshake either.

Port 8443 appeared to be quite similar to 8008, but with HTTPS: (note the certificate data)
$ curl -kv https://192.168.43.166:8443
*   Trying 192.168.43.166:8443...
* Connected to 192.168.43.166 (192.168.43.166) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=0838E6334B8C7315FFFF FA:8F:CA:7D:DD:81
*  start date: Mar 10 09:51:23 2021 GMT
*  expire date: Mar  5 09:51:23 2041 GMT
*  issuer: C=CN; ST=BJ; L=Beijing; O=LENOVO; OU=Cast; CN=LENOVO SmartClock Cast AIVision
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET / HTTP/1.1
> Host: 192.168.43.166:8443
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Content-Length:0
< Content-Type:text/html
<
* Connection #0 to host 192.168.43.166 left intact
Port 9000 speaks TLS but not HTTP(S):
$ curl -kv https://192.168.43.166:9000
*   Trying 192.168.43.166:9000...
* Connected to 192.168.43.166 (192.168.43.166) port 9000 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS alert, handshake failure (552):
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* Closing connection 0
curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure

Assuming I found nothing new here, I search the web a bit, and found a post called Google Home (in)Security from 2018 which explores the ports I found in some more depth. Based on the information in that post, I did a few more requests.

(Note that I rebooted meanwhile - the reboot command from that post worked! If you’re wondering why the IP is different in the pastes below, that is why.)

"eureka info"
$ curl  -kv https://192.168.43.225:8443/setup/eureka_info | jq
*   Trying 192.168.43.225:8443...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to 192.168.43.225 (192.168.43.225) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [91 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2924 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=0838E6334B8C7315FFFF FA:8F:CA:7D:DD:81
*  start date: Mar 10 09:51:23 2021 GMT
*  expire date: Mar  5 09:51:23 2041 GMT
*  issuer: C=CN; ST=BJ; L=Beijing; O=LENOVO; OU=Cast; CN=LENOVO SmartClock Cast AIVision
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
} [5 bytes data]
> GET /setup/eureka_info HTTP/1.1
> Host: 192.168.43.225:8443
> User-Agent: curl/7.74.0
> Accept: */*
> 
{ [5 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Access-Control-Allow-Headers:Content-Type
< Cache-Control:no-cache
< Content-Length:1202
< Content-Type:application/json
< 
{ [1202 bytes data]
100  1202  100  1202    0     0  15815      0 --:--:-- --:--:-- --:--:-- 16026
* Connection #0 to host 192.168.43.225 left intact
{
  "bssid": "",
  "build_version": "257161",
  "cast_build_revision": "1.52.257161",
  "closed_caption": {},
  "connected": false,
  "ethernet_connected": false,
  "has_update": false,
  "hotspot_bssid": "FA:8F:CA:7D:DD:81",
  "locale": "de-DE",
  "location": {
    "country_code": "EU",
    "latitude": 255,
    "longitude": 255
  },
  "mac_address": "00:00:00:00:00:00",
  "name": "LenovoCD-24502F1428",
  "opt_in": {
    "crash": true,
    "opencast": false,
    "stats": true
  },
  "public_key": "MIIBCgKCAQEAt5c+s7LxGZVX3WHhGPLOzX3JzmOGxQ7ZhRC4gCsnsIXxMPIkCMnodm6f3PyU09Vi/hDVyjYnviKJM+XiCHpc2NyvapYXIcX3DJhkqUeqA4HYYc7GiRLs3RoRxQ/T+w2aPq7poE3zKssZJF4iZN3ZtRdmO5a/raQMQ5vJerUd052LQVIsXKkarhGhz5xHHB0vd8ZdEB02sJygvs1AFbJVk3tEOC27QtlYqKd3X2nwqPUcoS/3bHwtqN0INGALGqvHgmVrvPXs9X5Q8ShBOtnIA4Kjhhup0x3V+HC/g5QXYjhiAED+a0vAuNobNpaeJ4GVaomqaWf1nvJSoKvxM6lsVwIDAQAB",
  "release_track": "",
  "setup_state": 10,
  "setup_stats": {
    "historically_succeeded": true,
    "num_check_connectivity": 0,
    "num_connect_wifi": 0,
    "num_connected_wifi_not_saved": 0,
    "num_initial_eureka_info": 0,
    "num_obtain_ip": 0
  },
  "ssdp_udn": "32b180df-c0e6-a3d9-be80-2d9d8b72831e",
  "ssid": "",
  "time_format": 1,
  "tos_accepted": false,
  "uma_client_id": "53f581f4-2499-4140-ae99-7feaa8afb847",
  "uptime": 129.722581,
  "version": 10,
  "wpa_configured": false,
  "wpa_state": 1
}
SSDP device description
$ curl   -kv http://192.168.43.225:8008/ssdp/device-desc.xml
*   Trying 192.168.43.225:8008...
* Connected to 192.168.43.225 (192.168.43.225) port 8008 (#0)
> GET /ssdp/device-desc.xml HTTP/1.1
> Host: 192.168.43.225:8008
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Application-URL:http://192.168.43.225:8008/apps/
< Content-Length:1085
< Content-Type:application/xml
< 
<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
  <specVersion>
    <major>1</major>
    <minor>0</minor>
  </specVersion>
  <URLBase>http://192.168.43.225:8008</URLBase>
  <device>
    <deviceType>urn:dial-multiscreen-org:device:dial:1</deviceType>
    <friendlyName>LenovoCD-24502F1428</friendlyName>
    <manufacturer>LENOVO</manufacturer>
    <modelName>LenovoCD-24502F</modelName>
    <UDN>uuid:32b180df-c0e6-a3d9-be80-2d9d8b72831e</UDN>
    <iconList>
      <icon>
        <mimetype>image/png</mimetype>
        <width>98</width>
        <height>55</height>
        <depth>32</depth>
        <url>/setup/icon.png</url>
      </icon>
    </iconList>
    <serviceList>
      <service>
        <serviceType>urn:dial-multiscreen-org:service:dial:1</serviceType>
        <serviceId>urn:dial-multiscreen-org:serviceId:dial</serviceId>
        <controlURL>/ssdp/notfound</controlURL>
        <eventSubURL>/ssdp/notfound</eventSubURL>
        <SCPDURL>/ssdp/notfound</SCPDURL>
      </service>
    </serviceList>
  </device>
</root>
* Connection #0 to host 192.168.43.225 left intact

Initial setup

After digging around in the pre-setup environment for a bit, I got my phone with the Google Home app, and proceeded to setup the device. In that process, the Clock downloaded and installed a firmware update, bumping the software version to 1.56.258116.

The `eureka_info` output looked like this for me after setup:
{
  "bssid": "20:e8:82:9b:da:c7",
  "build_version": "285116",
  "cast_build_revision": "1.56.285116",
  "closed_caption": {},
  "connected": true,
  "ethernet_connected": false,
  "has_update": false,
  "hotspot_bssid": "FA:8F:CA:7D:DD:81",
  "ip_address": "192.168.0.17",
  "locale": "en-GB",
  "location": {
    "country_code": "NL",
    "latitude": 255,
    "longitude": 255
  },
  "mac_address": "00:00:00:00:00:00",
  "name": "Office clock",
  "opt_in": {
    "crash": false,
    "opencast": false,
    "stats": false
  },
  "public_key": "MIIBCgKCAQEAt5c+s7LxGZVX3WHhGPLOzX3JzmOGxQ7ZhRC4gCsnsIXxMPIkCMnodm6f3PyU09Vi/hDVyjYnviKJM+XiCHpc2NyvapYXIcX3DJhkqUeqA4HYYc7GiRLs3RoRxQ/T+w2aPq7poE3zKssZJF4iZN3ZtRdmO5a/raQMQ5vJerUd052LQVIsXKkarhGhz5xHHB0vd8ZdEB02sJygvs1AFbJVk3tEOC27QtlYqKd3X2nwqPUcoS/3bHwtqN0INGALGqvHgmVrvPXs9X5Q8ShBOtnIA4Kjhhup0x3V+HC/g5QXYjhiAED+a0vAuNobNpaeJ4GVaomqaWf1nvJSoKvxM6lsVwIDAQAB",
  "release_track": "",
  "setup_state": 60,
  "setup_stats": {
    "historically_succeeded": true,
    "num_check_connectivity": 0,
    "num_connect_wifi": 0,
    "num_connected_wifi_not_saved": 0,
    "num_initial_eureka_info": 0,
    "num_obtain_ip": 0
  },
  "ssdp_udn": "32b180df-c0e6-a3d9-be80-2d9d8b72831e",
  "ssid": "<removed>",
  "time_format": 2,
  "timezone": "Europe/Amsterdam",
  "tos_accepted": true,
  "uptime": 1001.472884,
  "version": 12,
  "wpa_configured": true,
  "wpa_id": 0,
  "wpa_state": 10
}

(I removed the ssid from that output, but note I did not change mac_address, it really has all those zeroes!)

In this updated firmware, even after a factory reset, most of the nicely open APIs we found previously are now closed. Via the blog post I linked before, I found an inofficial API description that also suggests there are ways to authenticate to these APIs. I did not try this.

Teasers

Some teasers for (potential) upcoming posts about this device:

The Home Assistant Android app (with thanks to Guide installing android apps on the lenovo smart clock 2):

Home Assistant Android app

No-solder wired interfacing (in progress, waiting for USB sockets from AliExpress) - in case Lets get started on the Lenovo Smart Clock 2! goes anywhere useful.

Update 20 March 2023: I’ve abandoned my draft about installing Android apps because people have now done Youtube videos way more useful than what I had typed so far. The XDA thread links to at least one of them.

The best I’ve seen so far is Bedside Home Assistant Dashboard - Hacking the Lenovo Smart Clock 2 to run Android Apps, which not only shows two ways of getting enough access to install Android apps, it then goes on to seriously customise the device, getting rid of Google Home entirely. I did find a third way to install Android apps, which I’ve now written into a comment under that video, and repeated here:

If you swipe up from the bottom, tap the config wheel and choose “send feedback”, then say something like “install f-droid”, then tap “Account and system information”, your spoken text (‘install f-droid") appears there. Using the accessibility mode, select it, draw an L (start top left), then pick Copy. Then, once you’re in the browser (I used the same method - Privacy policy) you can click the button with the 9 squares and go to search. Then paste your text. This way you don’t need the calendar or a DNS trick.

(Once you install and run F-Droid, you do need to use that opportunity to immediately install a launcher and/or a keyboard, or you won’t be able to get back into F-Droid again, unless you do all the steps again.)

(Also, once you have a launcher, you can get to Android settings and pair a bluetooth keyboard, which is super convenient.)

updatedupdated2023-03-202023-03-20