Skip to main content

switchbot-control

❖ Communityv0.1.0

SwitchBot OpenAPI v1.1, personal use. No Plug Mini on/off. Locks off unless enabled. setPosition only Curtain/Curtain3 (0=open).

Open in Hermes Desktop
hermes plugins install switchbot-control

What it adds

Tools 3

switchbot_devicesswitchbot_commandswitchbot_watch

Environment variables it needs 2

SWITCHBOT_TOKENSWITCHBOT_SECRET

README

From the reviewed commit bb68cb4 ↗; it updates when the author re-pins.

switchbot-control

Hermes tools for the SwitchBot cloud OpenAPI (api.switch-bot.com, v1.1). Built on that API. This is not a SwitchBot product, and it does not use Home Assistant. Hermes has a separate Home Assistant plugin. This one does not speak BLE. The catalog card's local BLE path is a different route. Teknium's card says the cloud OpenAPI "requires a SwitchBot Hub plus Cloud Service enabled for BLE devices." The SwitchBot API README says the same for BLE devices, and Beyond V9.0 it names that menu Third-party Services. This plugin does not enable that setting. The live check's empty list was an account with zero devices. This plugin cannot tell that apart from the setting being off. SwitchBot is a trademark of its owner.

Authentication and error shapes were checked against the live SwitchBot API. Device status and commands follow the published examples and were not confirmed on hardware.

認証とエラーの形は実際の API で確かめた。機器の状態と操作は仕様の実例によるもので、実機では未確認。

What it does

  1. switchbot_devices lists physical devices and infrared remotes. An optional device_id also reads that device's status. It reports the list from that call and does not compare it with an earlier list, so a drop to zero is a successful empty list. Only switchbot_watch says when a previous watch file still has rows.
  2. switchbot_command sends one command from a fixed list, after Hermes approval.
  3. switchbot_watch reads meters, contact sensors, and plugs. It reports the plain Plug power string, not Plug Mini on or off. The cron prompt tells the job to call this tool only. The toolset still contains switchbot_command, and a cron context cannot send a command.

Signing

The sign that the live API accepted is the Python 3 example in the SwitchBotAPI README: base64 of HMAC-SHA256 over token + timestamp + nonce, keyed with the secret. Headers are Authorization, sign, t, and nonce. The short uppercase token + timestamp example in that README was not sent.

On 2026-10-06, with an account that had zero devices, these responses came back. The token and secret are not in this repository.

Call Result
Signed GET /v1.1/devices HTTP 200, statusCode 100, message success, deviceList 0, infraredRemoteList 0
Wrong token, wrong secret, wrong sign, or a timestamp 10 minutes old HTTP 401, JSON {"message": "Unauthorized"}, no statusCode
GET /v1.1/devices/000000000000/status HTTP 200, statusCode 100, message success, body {}
POST turnOn to that same id HTTP 200, statusCode 190, message Wrong deviceId, No this device, body {}

An empty status body is not reported as a device state. statusCode other than 100 is not success, including 190 on HTTP 200. A redirect is not followed. Each call times out at 20 seconds. There is no retry. A command POST that times out, or that gets back a 5xx, a 408, a redirect, a body that is not JSON, or HTTP 200 with a statusCode other than 100 (such as 190), may already have reached the device and moved it. The result says so and asks you to check the device state before you send it again. A 401 or another 4xx does not get that note. A body over 1000000 bytes is discarded. A watch tick makes at most 21 calls. If every call uses the full 20 second timeout, that is about 420 seconds. That figure is an estimate, not a cap. A command that is sent waits 20 seconds for the device list, then Hermes's approval wait, then 20 seconds for the command. That wait is approvals.timeout, and the default is 300 seconds, which is about 340 seconds. That figure is an estimate, not a cap. This plugin does not cap that setting. Raising it makes the command wait longer.

Commands

Allowed, with parameter default: turnOn, turnOff, press, pause, volumeAdd, volumeSub, channelAdd, channelSub, setMute, FastForward, Rewind, Next, Previous, Pause, Play, Stop.

Also allowed when the parameter matches a published v1.1 table: setPosition, setAll, and SetChannel (digits). setPosition is the Curtain and Curtain 3 form, 0,ff,0 through 0,ff,100, with mode 0, 1, or ff. It is not on the infrared table. Blind Tilt's up;60 and Roller Shade's 0 through 100 do not match that form, so they are refused. setPosition is sent only when the device type is Curtain or Curtain3. 0 is open and 100 is closed. setAll and SetChannel are on the infrared table. For setAll, that table says modes 0 and 1 are auto, then 2 cool, 3 dry, 4 fan, and 5 heat. Fan speed 1 is auto, 2 low, 3 medium, and 4 high. The example is 26,1,3,on. Temperature is Celsius. The table does not publish a minimum or maximum, so this plugin accepts 0 through 40.

lock, unlock, and deadbolt are refused unless config safety_devices is true. A device whose type token is lock, keypad, garage, or door is refused the same way, including turnOn on that device. User-defined infrared buttons (commandType customize) are refused. Anything else is refused. The check happens before a command POST.

Every allowed command still asks Hermes. Each call uses a new approval rule, so a previous always or session choice does not cover the next call. Choosing always writes one unused line into Hermes config.yaml command_allowlist on every call. Choose once. The plugin reads request_tool_approval and requires approved to be true. The approval text names the device, its type, and the parameter. For Curtain and Curtain 3 it also says what the command does: turnOff closes the curtain (position 100), turnOn opens it (position 0), and setPosition names the position. It does not change or cap Hermes's approval wait. The default approvals.timeout is 300 seconds. A higher value makes a sent command wait longer. Cron, yolo, approvals off, a single-query session, an unattended session, or a missing, renamed, or raising helper sends nothing. This plugin has no allowlist of its own. Hermes still applies its gateway rule: an empty platform allowlist denies the sender, except a per-platform allow-all, an approved pairing, a platform's own access check, or GATEWAY_ALLOW_ALL_USERS. Webhook events skip that rule, and so do Home Assistant events on Hermes 0.21.4 and events from a platform plugin marked trusted_inbound on newer Hermes. Hermes treats a webhook session as unattended, so it can list, read, and watch, but it cannot send a command. On Hermes 0.21.4 a Home Assistant session is not treated as unattended, so a command there reaches the approval step; the request is posted as a Home Assistant notification, nothing in that session can send /approve (it only receives [Home Assistant] … changed from … to … events), and Hermes refuses the command after approvals.timeout. A person who can already talk to the agent can request a command, and the approval happens in that chat.

The slash command is /switchbot-control. It can list devices, run one watch, or schedule and unschedule. It does not send a device command. Put the delivery target first, as in /switchbot-control schedule telegram every 10m. If the first word looks like a schedule (every, in, a number, *, @daily, or a weekday), no job is created. The CLI is hermes switchbot-control with devices, watch, schedule, and unschedule. Hermes plugins.isolation: host does not register that CLI. The default is in_process, where the CLI is registered. The slash command remains either way.

accepted: true means the API returned HTTP 200, statusCode 100, and message success. moved stays false. The plugin does not read the device after the command, so it does not claim the device moved.

Watch and the daily cap

The published personal cap is 10000 calls per day. The SwitchBot README says going over that limit returns Unauthorized. A 401 from this plugin therefore also tells you to check that account-wide cap. The over-cap body was not observed on the live checks. It counts its own calls on a UTC date, in usage.json, and writes the new count before the call. It cannot raise the cap. The first result that sees the remaining count at or below warn_remaining (default 1000) says so once that UTC day. SwitchBot's own reset timezone was not measured.

One watch tick is one device list plus up to max_status_reads status reads (default 8, hard max 20). A repeating cron schedule is refused unless this plugin can prove it is slow enough that those calls fit in the cap, and never faster than every 120 seconds. The default schedule is */5 * * * *, which is 288 schedule slots per day. Each cron run costs at least one model turn; a turn that calls a tool makes two or more model requests. At the default of every 5 minutes, that is at most 288 agent turns a day, and the prompt asks each one to call switchbot_watch, so 576 or more model requests a day. A slot that runs is one Hermes agent turn. If that run is still going, Hermes skips the next slot, so a slow watch does not produce 288 turns. create_job skips the agent with no_agent=True (that mode requires a script), when a script returns wakeAgent=false, or when monitor_script or monitor_url output is unchanged. This plugin passes none of those. One-shot delays are not a repeating load. The cron prompt tells the job to call switchbot_watch only. The toolset still contains switchbot_command, and a cron context cannot send a command.

Meter types follow the OpenAPI list names, including Meter, MeterPlus, MeterPro, MeterPro(CO2), and WoIOSensor (Outdoor Meter). Hub 2 can report temperature, and this plugin does not treat that hub as a meter. Contact is Contact Sensor. Plugs are types whose name contains Plug.

Meter alerts use temperature_high_c, temperature_low_c, and humidity_high_pct only when those strings are numbers. A value that is not a number, in any of the four, stops the device list, commands, the watch, and schedule before any API call. unschedule still works. Contact openState and the plain Plug power string are reported only after a change from the previous sample. That power string is on the Plug status table. Plug Mini (US) and Plug Mini (JP) status tables do not publish power or powerState. powerState is on their webhook tables, and this plugin does not receive webhooks, so it does not report those plugs turning on or off. Plug Mini (EU) on/off is switchStatus, which this plugin does not read. That device's power field is watts, a number. This plugin compares power only when it is a string, so a change in that number is not reported. Plug weight is the OpenAPI field described as power consumed in a day, in watts, and alerts only when plug_weight_watts is set and the value crosses it. A plug with no power string and no weight threshold produces no power alert.

If watch.json cannot be read, the tick says so and does not claim that nothing changed. If the previous file had device rows and this list has no meter, contact, or plug, the tick says the account is not a normal empty one. It does not delete those rows.

watch.json stores the device id, device type, and the fields that were read (temperature, humidity, openState, power, weight, electricCurrent, voltage). It also stores faults (device id and statusCode) and list_gap when the list has none of those devices but older rows remain. It does not store powerState. It does not store the token, the secret, or the device name. The chat still contains the tool result. The device list in that result includes names the API returned. At most 200 rows are kept.

Disclosure —

One SwitchBot account, from SWITCHBOT_TOKEN and SWITCHBOT_SECRET. No BLE. No Home Assistant. No plugin allowlist. An empty Hermes gateway allowlist denies the sender, except a per-platform allow-all, an approved pairing, a platform's own access check, or GATEWAY_ALLOW_ALL_USERS. Webhook events skip that rule, and so do Home Assistant events on Hermes 0.21.4 and events from a platform plugin marked trusted_inbound on newer Hermes. A webhook session cannot send a command. On Hermes 0.21.4 a Home Assistant session is not treated as unattended, so a command there reaches the approval step; the request is posted as a Home Assistant notification, nothing in that session can send /approve (it only receives [Home Assistant] … changed from … to … events), and Hermes refuses the command after approvals.timeout. Approval for a command is in that chat. While HERMES_PLUGIN_HOST_PROCESS is 1, commands are refused and no command API call is made. The slash command is /switchbot-control. The CLI is hermes switchbot-control. Cron cannot send a command. The SwitchBot API README limits this API to personal use and prohibits commercial use and large-scale applications. A watch failure (unauthorized, the daily cap, a broken counter, and the other watch errors) sets notify true on the first failure of a streak and false on the later ones. A later successful watch starts the streak over. While the streak lasts, notify turns true again once 24 hours have passed since the last notice. This plugin cannot tell whether Hermes delivered a notice. If one was not delivered, the streak stays quiet until the next 24-hour notice. hermes cron list shows a run whose result was not delivered. Wrong tool arguments are not counted in the streak. A command POST that times out, or gets a 5xx, a 408, a redirect, a body that is not JSON, or a statusCode other than 100, may have moved the device. Check before sending it again. An empty watch list (no meter, contact, or plug) notifies once per empty stretch and says that Cloud Services being off looks the same. Later quiet ticks of that stretch are not a healthy watch. A watch failure clears that empty count, so the next empty list notifies again. A device statusCode such as 161, 171, 190, or 152 is a note for that device and does not stop the tick. The same status on the next tick does not notify again. A 401, the daily cap, or a network failure stops the tick. If the previous file still has rows and the list has no meter, contact, or plug, that notice is sent once, until the list has those devices again. The agent can call the tools itself. There is no child process, so this is not a sandbox for untrusted code. No price list is fetched. Private modules (tools.approval, tools.approval_context, cron.jobs, plugins.plugin_storage) fail closed: if one is missing, renamed, or raises, no command is sent. tests/ is shipped and is not loaded by register(). Removing the plugin leaves the cron job, usage.json, watch.json, and watch_fail.json (the failure streak). Remove the job with hermes switchbot-control unschedule or hermes cron remove. hermes cron list only shows the job. Delete the switchbot-control plugin-data directory to remove those files.

Checked, and not checked

Checked on the live API: a correct signature, a wrong token, a wrong secret, a wrong signature, a timestamp 10 minutes old, an unknown device status, an unknown device command, and an empty device list.

Not checked on hardware: meter, contact, and plug readings, and every command. Those parsers follow the examples in the SwitchBotAPI repository. The account used for the live checks had no devices.

ja-writing-guard❖ Community★ 0

Rule-based proofreading for Japanese business text: finds and fixes AI-style phrasing (stock phrases, monotonous sentence endings, overused symbols and Markdown, English-calque grammar) with line, column and a fix hint. It checks wording, not authorship, and rarely flags plain model output. A transform_llm_output hook checks every final Japanese answer and only reports by default; opt-in enforce mode rewrites flagged answers with the user's own model and delivers the rewrite only if numbers, names, URLs, code and negation survive and the score drops under the threshold. Disclosure: no network calls of its own; enforce mode sends each flagged answer to the user's configured model (one extra call, a second only when the first rewrite fails the checks or stays over the threshold; extra cost and up to 25 s delay); a read-only post_tool_call hook notes turns with MEDIA: lines so attachments from other plugins are kept; reports stay in process memory on local surfaces only. Disclosure — no network calls of its own; mode report (default) only logs scores; opt-in mode enforce sends each flagged final answer (with finding excerpts) to the user's configured model via ctx.llm (1–3 calls, up to 25 s delay) and replaces the delivered and stored answer when its preservation checks pass, which do not prove meaning is unchanged; reads plugins.hook_callback_timeout via a private read-only Hermes helper; eval/ dev scripts ship in the tree but are never loaded.

Tools
jp-charts❖ Community★ 0

Bar, line, stacked and 100% band charts as phone-sized PNGs for chat replies and reports, with Japanese text, 万/億 units and a required unit and source line. Every bar, point, label and tick is read back from the finished figure and compared with the parsed data before the image is returned. Disclosure: no network calls at run time; reads a local .csv/.tsv/.txt/.json only when given its path (never hidden files or the Hermes home); writes PNGs to the Hermes image cache; a transform_llm_output hook adds a forgotten MEDIA line to replies on chat platforms and cron deliveries (setting auto_attach, on by default).

Tools
jp-chatwork❖ Community★ 0

Talk to Hermes from Chatwork rooms, built on the Chatwork API: put Hermes in a Chatwork room and ask it with [To:] or a reply, like a colleague; the answer comes back as a Chatwork reply. Polls the Chatwork API (no public URL or webhook), never answers a message twice across restarts, and while CHATWORK_ALLOWED_USERS is unset only the token's own account can use Hermes commands beyond /help, /whoami, /new and /reset or answer approval prompts. Disclosure: talks only to api.chatwork.com with your own API token; polls every 5 s by default while the gateway runs; stores read positions and the ids of its posted messages in <HERMES_HOME>/plugin-data/jp-chatwork/state.json. Disclosure — while CHATWORK_ALLOWED_USERS is unset everyone in the rooms listed in CHATWORK_ROOMS (external guests included) can prompt the agent and use its enabled tools, with Hermes commands and approval answers kept to the token's own account; talks only to api.chatwork.com with your API token, polling every 5 s while the gateway runs; stores read positions and posted message ids in <HERMES_HOME>/plugin-data/jp-chatwork/state.json; logs each asker's name and account id.

Platforms
jp-corporate❖ Community★ 0

Japanese corporate lookup (法人照会): find or verify a company by name or corporate number and read its gBizINFO record (basic info, subsidies, government contracts, awards, certifications, financials, patents, workplace data). Disclosure — sends the company names, corporate numbers and prefectures you look up to api.info.gbiz.go.jp (METI gBizINFO) with your own GBIZINFO_API_TOKEN in a request header; no other host, nothing written to disk.

Tools
jp-egov-law❖ Community★ 0

Japanese laws for Hermes Agent, built on the e-Gov Law API: find a law by name, abbreviation or phrase, read one article as in force today or on a given date, and check amendment history and enforcement dates, with the source credit e-Gov's terms ask for. Disclosure — sends the law names, article numbers, dates and search phrases you look up to laws.e-gov.go.jp (public API, no key); no other host, nothing written to disk.

Tools
jp-estat❖ Community★ 0

Japanese government statistics for Hermes Agent, built on the e-Stat API (政府統計の総合窓口, Japan's official government statistics portal): search tables, see their categories, and fetch numbers by name (population, households, establishments by industry, wages, consumer prices) with units, survey dates and citations. Disclosure — sends your search words and table selections to api.e-stat.go.jp with your own ESTAT_APP_ID (e-Stat requires the ID in the request URL); no other host, nothing written to disk.

Tools

← Back to the catalog · catalog built Oct 7, 2026