├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-request.md │ ├── feedback.md │ └── new-issue.md ├── dependabot.yml └── workflows │ ├── build.yml │ └── codeql-analysis.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── config.schema.json ├── eslint.config.js ├── lib ├── connection │ ├── http.js │ └── upnp.js ├── device │ ├── coffee.js │ ├── crockpot.js │ ├── dimmer.js │ ├── heater.js │ ├── humidifier.js │ ├── index.js │ ├── insight.js │ ├── lightswitch.js │ ├── link-bulb.js │ ├── link-hub.js │ ├── maker-garage.js │ ├── maker-switch.js │ ├── motion.js │ ├── outlet.js │ ├── purifier.js │ └── simulation │ │ ├── purifier-insight.js │ │ ├── purifier.js │ │ ├── switch-insight.js │ │ └── switch.js ├── fakegato │ ├── LICENSE │ ├── fakegato-history.js │ ├── fakegato-storage.js │ ├── fakegato-timer.js │ └── uuid.js ├── homebridge-ui │ ├── public │ │ └── index.html │ └── server.js ├── index.js ├── platform.js └── utils │ ├── colour.js │ ├── constants.js │ ├── eve-chars.js │ ├── functions.js │ └── lang-en.js ├── package-lock.json └── package.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bwp91 2 | patreon: bwp91 3 | ko_fi: bwp91 4 | custom: ['https://www.paypal.me/BenPotter'] 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ### What issue do you have? Please be as thorough and explicit as possible. 8 | 9 | ### Details of your setup. 10 | * Do you use Homebridge (with config-ui?) or HOOBS? 11 | 12 | * Which version of Homebridge/HOOBS do you have? 13 | 14 | * Which version of this plugin (homebridge-wemo) do you have? Has the issue started since upgrading from a previous version? 15 | 16 | * Which Wemo devices do you have that are causing issues? Please include product models if applicable. 17 | 18 | ### Please paste any relevant logs below. 19 | 27 | 28 | ``` 29 | 30 | ``` 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🌍 Homebridge Discord Server 4 | url: https://discord.gg/bHjKNkN 5 | about: Please ask and answer questions here. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🆕 Device/Feature Request 3 | about: Submit new ideas for the plugin or request support for a new device. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | * **Please explain your feature request in a one or two sentences.** 11 | 12 | * **Is your feature request related to a problem? Please describe.** 13 | 14 | * **Any particular Wemo devices that this relates to?** 15 | 16 | * **Anything else?** 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ℹ️ Information/Feedback Request 3 | about: Ask general information about this plugin or give valuable feedback. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚨 Problem/Bug Alert 3 | about: The plugin isn't working as expected or it's showing an error. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | 15 | ### What issue do you have? Please be as thorough and explicit as possible. 16 | 17 | ### Details of your setup. 18 | * Do you use (1) Homebridge UI-X (2) Homebridge CLI or (3) HOOBS? 19 | 20 | * Which version of Homebridge/HOOBS do you have? 21 | 22 | * Which platform do you run Homebridge/HOOBS on (e.g. Raspberry Pi/Windows/HOOBS Box)? Please also mention your version of Node.js/NPM if known. 23 | 24 | * Which version of this plugin (homebridge-wemo) do you have? Has the issue started since upgrading from a previous version? 25 | 26 | * Which Wemo devices do you have that are causing issues? Please include product models if applicable. 27 | 28 | ### Please paste any relevant logs below. 29 | 36 | 37 | ``` 38 | 39 | ``` 40 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: npm # See documentation for possible values 9 | directory: / # Location of package manifests 10 | schedule: 11 | interval: daily 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | # the Node.js versions to build on 12 | node-version: [18.x, 20.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | 22 | - name: Install dependencies 23 | run: npm install 24 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | push: 5 | branches: [latest] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [latest] 9 | schedule: 10 | - cron: '0 1 * * 5' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v3 33 | # Override language selection by uncommenting this and choosing your languages 34 | # with: 35 | # languages: go, javascript, csharp, python, cpp, java 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v3 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | # - run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v3 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nova 3 | .npm 4 | node_modules 5 | .idea 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | .nova 3 | .npm 4 | .idea 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 - 2025 Ben Potter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
4 | 5 | 6 | # homebridge-wemo 7 | 8 | Homebridge plugin to integrate Wemo devices into HomeKit 9 | 10 | [](https://www.npmjs.com/package/homebridge-wemo) 11 | [](https://github.com/homebridge-plugins/homebridge-wemo/wiki/Beta-Version) 12 | 13 | [](https://github.com/homebridge/homebridge/wiki/Verified-Plugins) 14 | [](https://plugins.hoobs.org/plugin/homebridge-wemo) 15 | 16 | [](https://www.npmjs.com/package/homebridge-wemo) 17 | [](https://discord.com/channels/432663330281226270/742733745743855627) 18 | 19 | 20 | 21 | ### Plugin Information 22 | 23 | - This plugin allows you to view and control your Wemo devices within HomeKit. The plugin: 24 | - does not require your Wemo credentials as uses local network discovery (SSDP) and local control 25 | - will attempt to control your devices via a local HTTP request 26 | - will attempt to establish a UPnP connection to your devices to listen for external changes (if disabled, HTTP polling is used) 27 | 28 | ### Prerequisites 29 | 30 | - To use this plugin, you will need to already have: 31 | - [Node](https://nodejs.org): latest version of `v18`, `v20` or `v22` - any other major version is not supported. 32 | - [Homebridge](https://homebridge.io): `v1.6` - refer to link for more information and installation instructions. 33 | - For the UPnP connection, make sure your Homebridge instance has an allocated IP from the same IP network or VLAN as your Wemo devices. Otherwise, you should disable the UPnP connection to avoid connection errors. 34 | 35 | ### Setup 36 | 37 | - [Installation](https://github.com/homebridge-plugins/homebridge-wemo/wiki/Installation) 38 | - [Configuration](https://github.com/homebridge-plugins/homebridge-wemo/wiki/Configuration) 39 | - [Beta Version](https://github.com/homebridge/homebridge/wiki/How-to-Install-Alternate-Plugin-Versions) 40 | - [Node Version](https://github.com/homebridge-plugins/homebridge-wemo/wiki/Node-Version) 41 | 42 | ### Features 43 | 44 | - [Supported Devices](https://github.com/homebridge-plugins/homebridge-wemo/wiki/Supported-Devices) 45 | 46 | ### Help/About 47 | 48 | - [Common Errors](https://github.com/homebridge-plugins/homebridge-wemo/wiki/Common-Errors) 49 | - [Support Request](https://github.com/homebridge-plugins/homebridge-wemo/issues/new/choose) 50 | - [Changelog](https://github.com/homebridge-plugins/homebridge-wemo/blob/latest/CHANGELOG.md) 51 | - [About Me](https://github.com/sponsors/bwp91) 52 | 53 | ### Credits 54 | 55 | - To the creator of this plugin: [@rudders](https://github.com/rudders), and to [@devbobo](https://github.com/devbobo) for his contributions. 56 | - To the creator of [wemo-client](https://github.com/timonreinhard/wemo-client) (which is now contained within this plugin): [@timonreinhard](https://github.com/timonreinhard). 57 | - To [Ben Hardill](http://www.hardill.me.uk/wordpress/tag/wemo/) for his research on Wemo devices. 58 | - To all users who have helped/tested to enable functionality for new devices. 59 | - To the creators/contributors of [Fakegato](https://github.com/simont77/fakegato-history): [@simont77](https://github.com/simont77) and [@NorthernMan54](https://github.com/NorthernMan54). 60 | - To the creator of the awesome plugin header logo: [Keryan Belahcene](https://www.instagram.com/keryan.me). 61 | - To the creators/contributors of [Homebridge](https://homebridge.io) who make this plugin possible. 62 | 63 | ### Disclaimer 64 | 65 | - I am in no way affiliated with Belkin/Wemo and this plugin is a personal project that I maintain in my free time. 66 | - Use this plugin entirely at your own risk - please see licence for more information. 67 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import { antfu } from '@antfu/eslint-config' 2 | 3 | /** @type {typeof antfu} */ 4 | export default antfu( 5 | { 6 | ignores: [], 7 | jsx: false, 8 | rules: { 9 | 'curly': ['error', 'multi-line'], 10 | 'new-cap': 'off', 11 | 'import/extensions': ['error', 'ignorePackages'], 12 | 'import/order': 0, 13 | 'jsdoc/check-alignment': 'warn', 14 | 'jsdoc/check-line-alignment': 'warn', 15 | 'jsdoc/require-returns-check': 0, 16 | 'jsdoc/require-returns-description': 0, 17 | 'no-undef': 'error', 18 | 'perfectionist/sort-exports': 'error', 19 | 'perfectionist/sort-imports': [ 20 | 'error', 21 | { 22 | groups: [ 23 | 'type', 24 | 'internal-type', 25 | 'builtin', 26 | 'external', 27 | 'internal', 28 | ['parent-type', 'sibling-type', 'index-type'], 29 | ['parent', 'sibling', 'index'], 30 | 'object', 31 | 'unknown', 32 | ], 33 | order: 'asc', 34 | type: 'natural', 35 | newlinesBetween: 'always', 36 | }, 37 | ], 38 | 'perfectionist/sort-named-exports': 'error', 39 | 'perfectionist/sort-named-imports': 'error', 40 | 'quotes': ['error', 'single'], 41 | 'sort-imports': 0, 42 | 'style/brace-style': ['error', '1tbs', { allowSingleLine: true }], 43 | 'style/quote-props': ['error', 'consistent-as-needed'], 44 | 'test/no-only-tests': 'error', 45 | 'unicorn/no-useless-spread': 'error', 46 | 'unused-imports/no-unused-vars': ['error', { caughtErrors: 'none' }], 47 | }, 48 | typescript: false, 49 | }, 50 | ) 51 | -------------------------------------------------------------------------------- /lib/connection/http.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import PQueue from 'p-queue' 3 | import { parseStringPromise } from 'xml2js' 4 | import xmlbuilder from 'xmlbuilder' 5 | 6 | import { decodeXML, hasProperty, parseError } from '../utils/functions.js' 7 | import platformLang from '../utils/lang-en.js' 8 | 9 | export default class { 10 | constructor(platform) { 11 | // Set up global vars from the platform 12 | this.platform = platform 13 | this.queue = new PQueue({ 14 | concurrency: 1, 15 | interval: 250, 16 | intervalCap: 1, 17 | timeout: 9000, 18 | throwOnTimeout: true, 19 | }) 20 | } 21 | 22 | async sendDeviceUpdate(accessory, serviceType, action, body) { 23 | try { 24 | return await this.queue.add(async () => { 25 | // Check the device has this service (it should have) 26 | if ( 27 | !accessory.context.serviceList[serviceType] 28 | || !accessory.context.serviceList[serviceType].controlURL 29 | ) { 30 | throw new Error(platformLang.noService) 31 | } 32 | 33 | // Generate the XML to send to the device 34 | const xml = xmlbuilder 35 | .create('s:Envelope', { 36 | version: '1.0', 37 | encoding: 'utf-8', 38 | allowEmpty: true, 39 | }) 40 | .att('xmlns:s', 'http://schemas.xmlsoap.org/soap/envelope/') 41 | .att('s:encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/') 42 | .ele('s:Body') 43 | .ele(`u:${action}`) 44 | .att('xmlns:u', serviceType) 45 | 46 | // Send the request to the device 47 | const hostPort = `http://${accessory.context.ipAddress}:${accessory.context.port}` 48 | const res = await axios({ 49 | url: hostPort + accessory.context.serviceList[serviceType].controlURL, 50 | method: 'post', 51 | headers: { 52 | 'SOAPACTION': `"${serviceType}#${action}"`, 53 | 'Content-Type': 'text/xml; charset="utf-8"', 54 | }, 55 | data: (body ? xml.ele(body) : xml).end(), 56 | timeout: 10000, 57 | }) 58 | 59 | // Parse the response from the device 60 | const xmlRes = res.data 61 | const response = await parseStringPromise(xmlRes, { 62 | explicitArray: false, 63 | }) 64 | 65 | if (!accessory.context.httpOnline) { 66 | this.platform.updateHTTPStatus(accessory, true) 67 | } 68 | 69 | // Return the parsed response 70 | return response['s:Envelope']['s:Body'][`u:${action}Response`] 71 | }) 72 | } catch (err) { 73 | const eText = parseError(err) 74 | if (['at Object.
2 |
7 |