🐦 Twitter Post Details

Viewing enriched Twitter post

@pol_avec

Why you should write the code to use your Thermostat Yesterday, I tried following @karpathy 's lead and use CC to interface with my thermostat. The experience wasn't great for me. Today I tried the opposite, I built it step-by-step almost completely manually using @answerdotai 's SolveIT. It was great. I spent a much longer time, say maybe 4-5h. But it was worth it, even though I have a perfectly fine app already to handle the temperature (in other words, I might not reuse the code again). So, was worth it? Yes, why? Because there were a myriad small decisions and lessons learned throughout the process. Those are usually small enough and that don't feel significant but they do compound in the end. They make your tools much sharper. If you look at the final package I published to pypi you won't see that, and it looks like code an LLM could one-shot, the LLM could definitely NOT make you learn during the process. This is more or less how it went: Well, I started getting SolveIT to read the docs for me and list which endpoints does the Thermostat API have. Unfortunately, their docs require JS to render 😅. I took this chance to have a look a Zyte service to give SolveIT a tool to read the docs. With that setup I got a list of all endpoints for the API. I followed the instructions to get the API keys & tokens. Gave it a try, it worked, I went away, and when I came back the token had expired. No problem, solveIT created a super short _refresh method as part of the class. Next step, create the basic "Home Status" info endpoint. But call _refresh first to make sure we have an updated token... I thought we would have to do this for all endpoints, so let's actually create a `_request` that does this for us already. That way the methods simply look like this: At this point I took the chance to practice using dialoghelper. I had created two endpoints following a clear format: first markdown header, @ patch method into the class, try it out and display results. Because the structure was super clear, and the full list of endpoints available already, SolveIT could add each one of the endpoints in the exact format I liked, fast & straightforward. At this point, when I finished, it felt silly, why did I spend time doing this at all, it was done so fast! but it was fast mostly because everything was perfectly set up. I was reminded of @johnowhitaker said during the course: " ... using SolveIT, we spend most of our time sharpening our tools..." and that felt very true, once your tools are sharp and set up for the task at hand, the task was super fast to finish. Thanks to all this I learned things I wouldn't otherwise: - using Zyte to scrape (& having it ready to reuse as an LLM tool) - best way to design this API (I decided to name the methods exactly like the endpoints to avoid cognitive overload) - how easy it is to actually control my thermostat, and having a package to quickly interface with it - got better a publishing w/ nbdev - learned about _proc folder The API point was an important one, I tried many different ways to structure the endpoint calls, for example saving the home_id into the class, or using the first one by default, etc... The time in-between coding sessions, I could feel my brain in the background considering different approaches. I ended settling on mimicking the endpoints as close as possible, and letting the user built on top. I feel like a better engineer & API designer as a result of this. This won't happen if CC creates everything for you. There's the expression "Death by a thousand paper cuts" which describes how numerous small, seemingly insignificant problems accumulate over time to cause major failure The opposite happens with this approach: countless small decisions and lessons that felt insignificant on their own, but compounded into something valuable. @jeremyphoward often says to treat everything as a learning opportunity, and this definitely feels like it.

Media 1
Media 2

📊 Media Metadata

{
  "media": [
    {
      "url": "https://crmoxkoizveukayfjuyo.supabase.co/storage/v1/object/public/media/posts/2005887203579248941/media_0.jpg?",
      "media_url": "https://crmoxkoizveukayfjuyo.supabase.co/storage/v1/object/public/media/posts/2005887203579248941/media_0.jpg?",
      "type": "photo",
      "filename": "media_0.jpg"
    },
    {
      "url": "https://crmoxkoizveukayfjuyo.supabase.co/storage/v1/object/public/media/posts/2005887203579248941/media_1.jpg?",
      "media_url": "https://crmoxkoizveukayfjuyo.supabase.co/storage/v1/object/public/media/posts/2005887203579248941/media_1.jpg?",
      "type": "photo",
      "filename": "media_1.jpg"
    }
  ],
  "processed_at": "2025-12-31T02:57:54.043641",
  "pipeline_version": "2.0"
}

🔧 Raw API Response

{
  "type": "tweet",
  "id": "2005887203579248941",
  "url": "https://x.com/pol_avec/status/2005887203579248941",
  "twitterUrl": "https://twitter.com/pol_avec/status/2005887203579248941",
  "text": "Why you should write the code to use your Thermostat\n\nYesterday, I tried following @karpathy 's lead and use CC to interface with my thermostat. The experience wasn't great for me.\n\nToday I tried the opposite, I built it step-by-step almost completely manually using @answerdotai 's SolveIT.\n\nIt was great. \n\nI spent a much longer time, say maybe 4-5h. But it was worth it, even though I have a perfectly fine app already to handle the temperature (in other words, I might not reuse the code again). So, was worth it? \n\nYes, why?\n\nBecause there were a myriad small decisions and lessons learned throughout the process. Those are usually small enough and that don't feel significant but they do compound in the end. They make your tools much sharper.\n\nIf you look at the final package I published to pypi you won't see that, and it looks like code an LLM could one-shot, the LLM could definitely NOT make you learn during the process.\n\nThis is more or less how it went:\n\nWell, I started getting SolveIT to read the docs for me and list which endpoints does the Thermostat API have.\n\nUnfortunately, their docs require JS to render 😅. I took this chance to have a look a Zyte service to give SolveIT a tool to read the docs.\n\nWith that setup I got a list of all endpoints for the API. I followed the instructions to get the API keys & tokens.\n\nGave it a try, it worked, I went away, and when I came back the token had expired. No problem, solveIT created a super short _refresh method as part of the class.\n\nNext step, create the basic \"Home Status\" info endpoint. But call _refresh first to make sure we have an updated token... \n\nI thought we would have to do this for all endpoints, so let's actually create a `_request` that does this for us already. That way the methods simply look like this:\n\nAt this point I took the chance to practice using dialoghelper. I had created two endpoints following a clear format: first markdown header, @ patch method into the class, try it out and display results. \n\nBecause the structure was super clear, and the full list of endpoints available already, SolveIT could add each one of the endpoints in the exact format I liked, fast & straightforward.\n\nAt this point, when I finished, it felt silly, why did I spend time doing this at all, it was done so fast! but it was fast mostly because everything was perfectly set up. I was reminded of @johnowhitaker said during the course:\n\n\" ... using SolveIT, we spend most of our time sharpening our tools...\" \n\nand that felt very true, once your tools are sharp and set up for the task at hand, the task was super fast to finish.\n\nThanks to all this I learned things I wouldn't otherwise:\n\n- using Zyte to scrape (& having it ready to reuse as an LLM tool)\n- best way to design this API (I decided to name the methods exactly like the endpoints to avoid cognitive overload)\n- how easy it is to actually control my thermostat, and having a package to quickly interface with it\n- got better a publishing w/ nbdev\n- learned about _proc folder\n\nThe API point was an important one, I tried many different ways to structure the endpoint calls, for example saving the home_id into the class, or using the first one by default, etc... The time in-between coding sessions, I could feel my brain in the background considering different approaches.\n\nI ended settling on mimicking the endpoints as close as possible, and letting the user built on top. I feel like a better engineer & API designer as a result of this. This won't happen if CC creates everything for you.\n\nThere's the expression \"Death by a thousand paper cuts\" which describes how numerous small, seemingly insignificant problems accumulate over time to cause major failure\n\nThe opposite happens with this approach: countless small decisions and lessons that felt insignificant on their own, but compounded into something valuable.\n\n@jeremyphoward often says to treat everything as a learning opportunity, and this definitely feels like it.",
  "source": "Twitter for iPhone",
  "retweetCount": 4,
  "replyCount": 3,
  "likeCount": 53,
  "quoteCount": 2,
  "viewCount": 15088,
  "createdAt": "Tue Dec 30 06:22:31 +0000 2025",
  "lang": "en",
  "bookmarkCount": 39,
  "isReply": false,
  "inReplyToId": null,
  "conversationId": "2005887203579248941",
  "displayTextRange": [
    0,
    304
  ],
  "inReplyToUserId": null,
  "inReplyToUsername": null,
  "author": {
    "type": "user",
    "userName": "pol_avec",
    "url": "https://x.com/pol_avec",
    "twitterUrl": "https://twitter.com/pol_avec",
    "id": "973515620384944129",
    "name": "Pol Avec",
    "isVerified": false,
    "isBlueVerified": true,
    "verifiedType": null,
    "profilePicture": "https://pbs.twimg.com/profile_images/1803309719324364800/KKu5rWf4_normal.jpg",
    "coverPicture": "https://pbs.twimg.com/profile_banners/973515620384944129/1597774262",
    "description": "",
    "location": "Barcelona",
    "followers": 555,
    "following": 197,
    "status": "",
    "canDm": true,
    "canMediaTag": true,
    "createdAt": "Tue Mar 13 11:06:25 +0000 2018",
    "entities": {
      "description": {
        "urls": []
      },
      "url": {}
    },
    "fastFollowersCount": 0,
    "favouritesCount": 3488,
    "hasCustomTimelines": true,
    "isTranslator": false,
    "mediaCount": 270,
    "statusesCount": 2216,
    "withheldInCountries": [],
    "affiliatesHighlightedLabel": {},
    "possiblySensitive": false,
    "pinnedTweetIds": [
      "1979236197634347218"
    ],
    "profile_bio": {
      "description": "鵬仁。Developing ML for fun. And enjoying the wild ride @fewsats. AI, space, and the transcend. Zima Blue",
      "entities": {
        "description": {
          "user_mentions": [
            {
              "id_str": "0",
              "indices": [
                53,
                61
              ],
              "name": "",
              "screen_name": "fewsats"
            }
          ]
        },
        "url": {
          "urls": [
            {
              "display_url": "justreadthe.info",
              "expanded_url": "http://justreadthe.info",
              "indices": [
                0,
                23
              ],
              "url": "https://t.co/O3Br2eg2nk"
            }
          ]
        }
      }
    },
    "isAutomated": false,
    "automatedBy": null
  },
  "extendedEntities": {
    "media": [
      {
        "allow_download_status": {
          "allow_download": true
        },
        "display_url": "pic.twitter.com/Rgk8OXeLZ5",
        "expanded_url": "https://twitter.com/pol_avec/status/2005887203579248941/photo/1",
        "ext_media_availability": {
          "status": "Available"
        },
        "features": {
          "large": {},
          "orig": {}
        },
        "id_str": "2005880348484247552",
        "indices": [
          305,
          328
        ],
        "media_key": "3_2005880348484247552",
        "media_results": {
          "id": "QXBpTWVkaWFSZXN1bHRzOgwAAQoAARvWUYyhVyAACgACG9ZXyLTXwS0AAA==",
          "result": {
            "__typename": "ApiMedia",
            "id": "QXBpTWVkaWE6DAABCgABG9ZRjKFXIAAKAAIb1lfItNfBLQAA",
            "media_key": "3_2005880348484247552"
          }
        },
        "media_url_https": "https://pbs.twimg.com/media/G9ZRjKFXIAADrEV.png",
        "original_info": {
          "focus_rects": [
            {
              "h": 116,
              "w": 207,
              "x": 243,
              "y": 0
            },
            {
              "h": 116,
              "w": 116,
              "x": 288,
              "y": 0
            },
            {
              "h": 116,
              "w": 102,
              "x": 295,
              "y": 0
            },
            {
              "h": 116,
              "w": 58,
              "x": 317,
              "y": 0
            },
            {
              "h": 116,
              "w": 660,
              "x": 0,
              "y": 0
            }
          ],
          "height": 116,
          "width": 660
        },
        "sizes": {
          "large": {
            "h": 116,
            "w": 660
          }
        },
        "type": "photo",
        "url": "https://t.co/Rgk8OXeLZ5"
      },
      {
        "allow_download_status": {
          "allow_download": true
        },
        "display_url": "pic.twitter.com/Rgk8OXeLZ5",
        "expanded_url": "https://twitter.com/pol_avec/status/2005887203579248941/photo/1",
        "ext_media_availability": {
          "status": "Available"
        },
        "features": {
          "large": {},
          "orig": {}
        },
        "id_str": "2005881136673652736",
        "indices": [
          305,
          328
        ],
        "media_key": "3_2005881136673652736",
        "media_results": {
          "id": "QXBpTWVkaWFSZXN1bHRzOgwAAQoAARvWUkQlFwAACgACG9ZXyLTXwS0AAA==",
          "result": {
            "__typename": "ApiMedia",
            "id": "QXBpTWVkaWE6DAABCgABG9ZSRCUXAAAKAAIb1lfItNfBLQAA",
            "media_key": "3_2005881136673652736"
          }
        },
        "media_url_https": "https://pbs.twimg.com/media/G9ZSRCUXAAAKTcS.jpg",
        "original_info": {
          "focus_rects": [
            {
              "h": 398,
              "w": 711,
              "x": 314,
              "y": 0
            },
            {
              "h": 398,
              "w": 398,
              "x": 470,
              "y": 0
            },
            {
              "h": 398,
              "w": 349,
              "x": 495,
              "y": 0
            },
            {
              "h": 398,
              "w": 199,
              "x": 570,
              "y": 0
            },
            {
              "h": 398,
              "w": 1410,
              "x": 0,
              "y": 0
            }
          ],
          "height": 398,
          "width": 1410
        },
        "sizes": {
          "large": {
            "h": 398,
            "w": 1410
          }
        },
        "type": "photo",
        "url": "https://t.co/Rgk8OXeLZ5"
      }
    ]
  },
  "card": null,
  "place": {},
  "entities": {
    "user_mentions": [
      {
        "id_str": "33836629",
        "indices": [
          83,
          92
        ],
        "name": "Andrej Karpathy",
        "screen_name": "karpathy"
      },
      {
        "id_str": "1765834686209925120",
        "indices": [
          267,
          279
        ],
        "name": "Answer.AI",
        "screen_name": "answerdotai"
      },
      {
        "id_str": "4004364327",
        "indices": [
          2380,
          2394
        ],
        "name": "Jonathan Whitaker",
        "screen_name": "johnowhitaker"
      },
      {
        "id_str": "175282603",
        "indices": [
          3870,
          3884
        ],
        "name": "Jeremy Howard",
        "screen_name": "jeremyphoward"
      }
    ]
  },
  "quoted_tweet": {
    "type": "tweet",
    "id": "2005505869023228380",
    "url": "https://x.com/pol_avec/status/2005505869023228380",
    "twitterUrl": "https://twitter.com/pol_avec/status/2005505869023228380",
    "text": "I tried this too by asking CC to connect to my netatmo thermostat.\n\nCC spent a huge amount of tokens scanning ports, using nmap, arp, dsn, web searching for my thermostat's brand MAC prefix, etc...\n\nIt did find my thermostat MAC address, which looked cool. Later, it walked me through the official way of creating an 'App' in the netatmo and getting the keys & tokens.\n\nAt this point the whole message quota almost ran out. \n\nThen it hit me that I was just going to use their standard API, so why the MAC and all that stuff. Effectively, all that exploration is pretty useless as CC recognizes, just a token sink.\n\nIt would have been much quicker to have a look at the docs from the beginning and set a quick python script to try it out.\n\nIt feels kind of useless to have wasted this amount of tokens for nothing, on the other hand, it's true that I would never have thought of trying to use my thermostat with an API (I have a perfectly ok app to do so) w/o CC, both because the original tweet was caused by CC and because I could just spin it up on the background while I did other stuff.\n\nBittersweet.",
    "source": "Twitter for iPhone",
    "retweetCount": 0,
    "replyCount": 0,
    "likeCount": 2,
    "quoteCount": 1,
    "viewCount": 15402,
    "createdAt": "Mon Dec 29 05:07:14 +0000 2025",
    "lang": "en",
    "bookmarkCount": 0,
    "isReply": false,
    "inReplyToId": null,
    "conversationId": "2005505869023228380",
    "displayTextRange": [
      0,
      301
    ],
    "inReplyToUserId": null,
    "inReplyToUsername": null,
    "author": {
      "type": "user",
      "userName": "pol_avec",
      "url": "https://x.com/pol_avec",
      "twitterUrl": "https://twitter.com/pol_avec",
      "id": "973515620384944129",
      "name": "Pol Avec",
      "isVerified": false,
      "isBlueVerified": true,
      "verifiedType": null,
      "profilePicture": "https://pbs.twimg.com/profile_images/1803309719324364800/KKu5rWf4_normal.jpg",
      "coverPicture": "https://pbs.twimg.com/profile_banners/973515620384944129/1597774262",
      "description": "",
      "location": "Barcelona",
      "followers": 555,
      "following": 197,
      "status": "",
      "canDm": true,
      "canMediaTag": true,
      "createdAt": "Tue Mar 13 11:06:25 +0000 2018",
      "entities": {
        "description": {
          "urls": []
        },
        "url": {}
      },
      "fastFollowersCount": 0,
      "favouritesCount": 3488,
      "hasCustomTimelines": true,
      "isTranslator": false,
      "mediaCount": 270,
      "statusesCount": 2216,
      "withheldInCountries": [],
      "affiliatesHighlightedLabel": {},
      "possiblySensitive": false,
      "pinnedTweetIds": [
        "1979236197634347218"
      ],
      "profile_bio": {
        "description": "鵬仁。Developing ML for fun. And enjoying the wild ride @fewsats. AI, space, and the transcend. Zima Blue",
        "entities": {
          "description": {
            "user_mentions": [
              {
                "id_str": "0",
                "indices": [
                  53,
                  61
                ],
                "name": "",
                "screen_name": "fewsats"
              }
            ]
          },
          "url": {
            "urls": [
              {
                "display_url": "justreadthe.info",
                "expanded_url": "http://justreadthe.info",
                "indices": [
                  0,
                  23
                ],
                "url": "https://t.co/O3Br2eg2nk"
              }
            ]
          }
        }
      },
      "isAutomated": false,
      "automatedBy": null
    },
    "extendedEntities": {
      "media": [
        {
          "allow_download_status": {
            "allow_download": true
          },
          "display_url": "pic.twitter.com/Pu7d9l3aFe",
          "expanded_url": "https://twitter.com/pol_avec/status/2005505869023228380/photo/1",
          "ext_media_availability": {
            "status": "Available"
          },
          "features": {
            "large": {},
            "orig": {}
          },
          "id_str": "2005504752801533952",
          "indices": [
            302,
            325
          ],
          "media_key": "3_2005504752801533952",
          "media_results": {
            "id": "QXBpTWVkaWFSZXN1bHRzOgwAAQoAARvU+/JyF9AACgACG9T89lYXMdwAAA==",
            "result": {
              "__typename": "ApiMedia",
              "id": "QXBpTWVkaWE6DAABCgABG9T78nIX0AAKAAIb1Pz2Vhcx3AAA",
              "media_key": "3_2005504752801533952"
            }
          },
          "media_url_https": "https://pbs.twimg.com/media/G9T78nIX0AAhKeT.jpg",
          "original_info": {
            "focus_rects": [
              {
                "h": 950,
                "w": 1696,
                "x": 273,
                "y": 0
              },
              {
                "h": 950,
                "w": 950,
                "x": 646,
                "y": 0
              },
              {
                "h": 950,
                "w": 833,
                "x": 705,
                "y": 0
              },
              {
                "h": 950,
                "w": 475,
                "x": 884,
                "y": 0
              },
              {
                "h": 950,
                "w": 3452,
                "x": 0,
                "y": 0
              }
            ],
            "height": 950,
            "width": 3452
          },
          "sizes": {
            "large": {
              "h": 564,
              "w": 2048
            }
          },
          "type": "photo",
          "url": "https://t.co/Pu7d9l3aFe"
        }
      ]
    },
    "card": null,
    "place": {},
    "entities": {},
    "quoted_tweet": {
      "type": "tweet",
      "id": "2005067301511630926",
      "url": "",
      "twitterUrl": "",
      "text": "",
      "source": "Twitter for iPhone",
      "retweetCount": 0,
      "replyCount": 0,
      "likeCount": 0,
      "quoteCount": 0,
      "viewCount": 0,
      "createdAt": "",
      "lang": "",
      "bookmarkCount": 0,
      "isReply": false,
      "inReplyToId": null,
      "conversationId": "",
      "displayTextRange": [],
      "inReplyToUserId": null,
      "inReplyToUsername": null,
      "author": {},
      "extendedEntities": {},
      "card": null,
      "place": {},
      "entities": {},
      "quoted_tweet": null,
      "retweeted_tweet": null,
      "isLimitedReply": false,
      "article": null
    },
    "retweeted_tweet": null,
    "isLimitedReply": false,
    "article": null
  },
  "retweeted_tweet": null,
  "isLimitedReply": false,
  "article": null
}