๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
IoT ๐Ÿ /Homebridge

[JS] Homebridge Smartthings ์—ฐ๋™ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ œ์ž‘, ์•…์„ธ์„œ๋ฆฌ ๋งคํ•‘ - 2

by GeekSean 2026. 1. 22.
๋ฐ˜์‘ํ˜•

[IoT ๐Ÿ /Homebridge] - [JS] Homebridge Smartthings ์—ฐ๋™ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ œ์ž‘, OAuth ์ธ์ฆ - 1

[IoT ๐Ÿ /Homebridge] - [JS] Homebridge Smartthings ์—ฐ๋™ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ œ์ž‘, ์•…์„ธ์„œ๋ฆฌ ๋งคํ•‘ - 2

 

์ €๋ฒˆ ๊ฒŒ์‹œ๊ธ€์—์„œ, ํ”„๋ก์‹œ ์„œ๋ฒ„์™€ OAuth ๋ฐฉ์‹์„ ์ด์šฉํ•˜์—ฌ

์˜๊ตฌ์ ์œผ๋กœ ํ† ํฐ์„ ์ž๋™์œผ๋กœ ๋ฐœ๊ธ‰/๊ฐฑ์‹ ํ•˜์—ฌ ๊ถŒํ•œ์„ ์œ ์ง€ํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

 

์ด๋ฒˆ์—๋Š” ์‹ค์ œ ๋ฐœ๊ธ‰๋œ ํ† ํฐ์„ ๊ฐ€์ง€๊ณ  Smartthings API๋ฅผ ์ด์šฉํ•ด,

๋””๋ฐ”์ด์Šค๋ฅผ ์กฐํšŒํ•˜๊ณ , ํ•ด๋‹น ๊ธฐ๊ธฐ๋“ค์„ Homebridge ์•…์„ธ์„œ๋ฆฌ์— ๋งคํ•‘ํ•˜์—ฌ

์‹ค์ œ๋กœ Smartthings์˜ Device๋“ค์„ ์—ฐ๋™ํ•ด๋ณด๋„๋ก ํ•˜์ž.

 

 

Smartthings API

 

์—ญ์‹œ ์ด๋ฒˆ์—๋„, API ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•ด์„œ

Device๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ช…๋ น๊ณผ, ๋™์ž‘์‹œํ‚ค๋Š” ๋ช…๋ น์–ด๋ฅผ ์ฐพ์•„ ๋ณด์•˜๋‹ค.

https://developer.smartthings.com/docs/api/public

 

API | Developer Documentation | SmartThings

SmartThings Public API

developer.smartthings.com

 

๋จผ์ € Postman์„ ์‚ฌ์šฉํ•ด, ํ—ค๋”์— ํ† ํฐ์„ ๋„ฃ์–ด ๋””๋ฐ”์ด์Šค ์กฐํšŒ ์š”์ฒญ์„ ๋„ฃ์–ด๋ณด์•˜๋‹ค.

https://api.smartthings.com/v1/devices
 

 

์š”์ฒญ ๊ฒฐ๊ณผ 200, ์ •์ƒ์ ์œผ๋กœ ๋””๋ฐ”์ด์Šค ๋ชฉ๋ก์ด ๋‚˜์™”๋‹ค.

 

ํ•„์ž๋Š” ์ง์ ‘ TV์™€ Set-Top, ์—์–ด์ปจ์„ ์ง์ ‘ Smartthings์— ์—ฐ๊ฒฐ์ด ์•„๋‹Œ

๊ฐค๋Ÿญ์‹œ ํ™ˆ ๋ฏธ๋‹ˆ์˜ IR ๋ช…๋ น์œผ๋กœ ์‚ฌ์šฉ์ค‘์ด๊ธฐ ๋•Œ๋ฌธ์—,

capabilites์— "stateless-" ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ช…๋ น์ด ๋งŽ์ด ๋‚˜์™”๋‹ค.

 

API ๋ฌธ์„œ์—์„œ ํ™•์ธ ๊ฒฐ๊ณผ, deprecated ๋œ ๋ช…๋ น๋“ค์ด์–ด์„œ

์ง์ ‘ github๋ฌธ์„œ๋ฅผ ์ฐพ์•„, ํ•ด๋‹น capablities id๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” command๋“ค์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ฐพ์•„์•ผ ํ–ˆ๋‹ค.

https://github.com/hongtat/smartthings-capabilities/tree/master

 

GitHub - hongtat/smartthings-capabilities: SmartThings Capabilities

SmartThings Capabilities. Contribute to hongtat/smartthings-capabilities development by creating an account on GitHub.

github.com

 

ํ•„์š”ํ•œ ์ •๋ณด๋Š” ์–ผ์ถ” ์ฐพ์•˜์œผ๋‹ˆ ์ด์ œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋งŒ๋“ค์–ด๋ณด์ž

 

 

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ œ์ž‘ (๋””๋ฐ”์ด์Šค ์ œ์–ด)

 

์ €๋ฒˆ์— ๋งŒ๋“ค๋˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ์ด์ œ ๋””๋ฐ”์ด์Šค ์กฐํšŒ์™€,

๊ฐ๊ฐ์˜ ๋””๋ฐ”์ด์Šค๋“ค์„ ์• ํ”Œ ํ™ˆ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์•…์„ธ์„œ๋ฆฌ๋ฅผ ๋งคํ•‘ํ•ด์ค„ ๊ฒƒ์ด๋‹ค.

 

index.js ํŒŒ์ผ์— ๋””๋ฐ”์ด์Šค๋ฅผ ์ฐพ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ ,

postman์—์„œ ์š”์ฒญ์‹œ ๋ฆฌํ„ด๋ฐ›์€ json ๊ฐ’์— ๋งž๊ฒŒ ์›ํ•˜๋Š” ๋””๋ฐ”์ด์Šค๋งŒ ๋งคํ•‘ํ•˜๋„๋ก ์ถ”์ถœํ–ˆ๋‹ค.

์ด๋•Œ, Homekit ๊ทœ๊ฒฉ์— ๋งž๊ฒŒ TV์™€ SetTop ์•…์„ธ์„œ๋ฆฌ๋Š” ๋ธŒ๋ฆฟ์ง€ ํ˜•ํƒœ๊ฐ€ ์•„๋‹Œ ์™ธ๋ถ€๊ธฐ๊ธฐ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋‹ค.

async discoverDevices() {
    const url = 'https://api.smartthings.com/v1/devices';
    try {
        const response = await axios.get(url, {
            headers: { 'Authorization': `Bearer ${this.accessToken}` }
        });
        const devices = response.data.items;

        for (const device of devices) {
            const caps = device.components?.[0]?.capabilities?.map(c => c.id) || [];
            const cats = device.components?.[0]?.categories?.map(c => c.name) || [];
            let AccessoryClass = null;

            if (cats.includes('Television')) AccessoryClass = TVAccessory;
            else if (cats.includes('SetTop')) AccessoryClass = SetTopAccessory;
            else if (caps.includes('airConditionerMode')) AccessoryClass = AirConAccessory;
            else if (caps.includes('switch') && caps.includes('powerMeter')) AccessoryClass = PlugAccessory;

            if (!AccessoryClass) continue; // ์‚ฌ์šฉ ์•ˆํ•  ๊ธฐ๊ธฐ Skip

            const isExternal = ['TVAccessory', 'SetTopAccessory'].includes(AccessoryClass.name);

            if (isExternal) {
                new AccessoryClass(this, device);
            } else {
                const uuid = this.api.hap.uuid.generate(device.deviceId);
                let existingAccessory = this.accessories.find(acc => acc.UUID === uuid);

                if (existingAccessory) {
                    new AccessoryClass(this, existingAccessory, device);
                } else {
                    const accessory = new this.api.platformAccessory(device.label, uuid);
                    new AccessoryClass(this, accessory, device);
                    this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
                    this.accessories.push(accessory);
                }
            }
        }
    } catch (err) {
        this.log.error('๊ธฐ๊ธฐ ๋ชฉ๋ก ๋กœ๋“œ ์‹คํŒจ:', err.message);
    }
}
 

 

์ด์ œ BaseAccessory ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด

๊ฐ๊ฐ์˜ ์•…์„ธ์„œ๋ฆฌ ์ œ์–ด ์š”์ฒญ์„ ํ•˜๋‚˜๋กœ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๋ณด์ž

 

๊ตฌ์กฐ์ฒด ์•ˆ์—, ๊ฐ ๊ธฐ๊ธฐ์—์„œ ์ œ์–ด์‹œ ์‚ฌ์šฉํ•  api ์š”์ฒญ ๋กœ์ง์„ ๋งŒ๋“ค๊ณ 

this.client = axios.create({
    baseURL: `https://api.smartthings.com/v1/devices/${this.deviceId}`,
    headers: { 'Authorization': `Bearer ${this.platform.accessToken}` }
});
 

 

์ €๋ฒˆ ๊ฒŒ์‹œ๊ธ€์—์„œ ๋งŒ๋ฃŒ๋œ ํ† ํฐ์ด ์žˆ์„์‹œ, ํ† ํฐ์„ ์žฌ๋ฐœ๊ธ‰ ํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ

401 ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ(ํ† ํฐ๋งŒ๋ฃŒ), ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ๋™์ž‘์‹œํ‚ฌ ์ธํ„ฐ์…‰ํ„ฐ๋„ ๊ตฌ์กฐ์ฒด์— ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

this.client.interceptors.response.use((response) => response,
    async (err) => {
        const originalRequest = error.config;

        if (error.response && error.response.status === 401 && !originalRequest._retry) {
            originalRequest._retry = true;
            try {
                const newToken = await this.platform.refreshAccessToken();
                originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
                this.client.defaults.headers['Authorization'] = `Bearer ${newToken}`;

                return this.client(originalRequest);
            } catch (err) {
                this.log.error('ํ† ํฐ ๊ฐฑ์‹  ์‹คํŒจ. ์ˆ˜๋™ ์ธ์ฆ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.');
            }
        }
        return Promise.reject(err);
    }
);
 

 

๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ๊ฐ์˜ ์•…์„ธ์„œ๋ฆฌ์—์„œ ๋ช…๋ น์„ ๋ณด๋‚ด๋Š” ๋™์ž‘๋„ ๊ตฌํ˜„

async executeCommand(capability, command, args = []) {
    try {
        await this.client.post('/commands', {
            commands: [{ component: 'main', capability, command, arguments: args }]
        });
    } catch (err) {
        this.log.error(`[${this.name}] ๋ช…๋ น ์‹คํŒจ: ${err.message}`);
    }
}
 

 

 

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ œ์ž‘ (์•…์„ธ์„œ๋ฆฌ ๋งคํ•‘)

 

์ด์ œ ์ œ์–ด ๋™์ž‘์„ ์™„๋ฃŒํ–ˆ์œผ๋‹ˆ,

ํ•„์š”ํ•œ ์•…์„ธ์„œ๋ฆฌ๋ฅผ Homebridge์— ๋งž๊ฒŒ ๋งคํ•‘ํ•ด๋ณด์ž

์‚ฌ์šฉํ•  ๊ธฐ๊ธฐ๋Š” TV, Set-Top, ์—์–ด์ปจ, ํ”Œ๋Ÿฌ๊ทธ 4๊ฐœ์ด๋ฏ€๋กœ ๊ฐ๊ฐ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๋‹ค.

 

๋จผ์ € TV ์•…์„ธ์„œ๋ฆฌ, ์ด์ „๋ถ€ํ„ฐ TV ์•…์„ธ์„œ๋ฆฌ๋Š” ํ•ญ์ƒ ๋งŒ๋“ค๋•Œ๋งˆ๋‹ค ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ๋Š”๋ฐ,

API ๋ฌธ์„œ๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์ •๋…ํ•ด๋ณธ ๊ฒฐ๊ณผ, ์–˜๋Š” ๋ฌด์กฐ๊ฑด ๊ฐœ๋ณ„ ์•…์„ธ์„œ๋ฆฌ๋กœ ๋“ฑ๋กํ•ด์•ผ ๋œ๋‹ค๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

๊นŒ๋‹ค๋กญ๊ฒŒ๋„ ๋งคํ•‘๋„ ํ‹€๋ฆผ ์—†์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์ค˜์•ผ ํ•˜๋ฏ€๋กœ, ์ฐจ๊ทผ์ฐจ๊ทผ ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

https://developers.homebridge.io/#/service/Television

 

Homebridge Plugin Developer Documentation

Homebridge plugin developer documentation and API reference.

developers.homebridge.io

 

๋ถ€๋ชจ ํด๋ž˜์Šค์ธ BaseAccessory ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๊ณ ,

์•…์„ธ์„œ๋ฆฌ ํƒ€์ž…์€ Television, ๊ทธ๋ฆฌ๊ณ  ๊ตฌ์กฐ์ฒด์— ๋™์ž‘๋“ค์„ ๊ฐ๊ฐ ๋งคํ•‘ํ–ˆ๋‹ค.

 

์•„๊นŒ Smartthings API์—์„œ ํ™•์ธํ•œ ์ •๋ณด์— ๋งž๊ฒŒ

stateless ์ „์›์—๋Š” "setButton", command์—๋Š” 'powerToggle'์ด ๋“ค์–ด๊ฐ€์•ผ ํ–ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ํ™ˆํ‚ท์˜ ๋ฆฌ๋ชจ์ปจ ์•…์„ธ์„œ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•…์„ธ์„œ๋ฆฌ๊ฐ€ ์ผœ์ง์ƒํƒœ์ด์—ฌ์•ผ ํ•˜์ง€๋งŒ,

IR ๋ช…๋ น์€ ์ƒํƒœ๊ฐ€ ์—†๋Š” ๋ฐฉ์‹์ด๋ฏ€๋กœ ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ค๋„ ํ•ญ์‹œ ์ผœ์ง์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜์˜€๋‹ค.

this.accessory = new this.platform.api.platformAccessory(this.name, uuid);
this.accessory.category = Categories.TELEVISION;

this.tvService.getCharacteristic(Characteristic.Active)
    .onGet(() => Characteristic.Active.ACTIVE)
    .onSet(async (value) => {
        await this.executeCommand('statelessPowerToggleButton', 'setButton', ['powerToggle']);

        if (value === Characteristic.Active.INACTIVE) {
            setTimeout(() => {
                this.tvService.updateCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE);
            }, 1000);
        }
    });
 

 

๊ทธ๋ฆฌ๊ณ  ํ™ˆํ‚ท์˜ ๋ฆฌ๋ชจ์ปจ ์•…์„ธ์„œ๋ฆฌ์˜ ์‹ญ์ž ๋ชจํ˜•์— ๊ฐ๊ฐ ์ฑ„๋„ ๋ณ€๊ฒฝ๊ณผ, ๋ณผ๋ฅจ ์กฐ์ ˆ์„ ๋งคํ•‘ํ–ˆ๋‹ค.

this.tvService.getCharacteristic(Characteristic.RemoteKey)
    .onSet(async (value) => {
        const cmdMap = {
            [Characteristic.RemoteKey.ARROW_UP]: ['statelessAudioVolumeButton', 'volumeUp'],
            [Characteristic.RemoteKey.ARROW_DOWN]: ['statelessAudioVolumeButton', 'volumeDown'],
            [Characteristic.RemoteKey.ARROW_LEFT]: ['statelessChannelButton', 'channelDown'],
            [Characteristic.RemoteKey.ARROW_RIGHT]: ['statelessChannelButton', 'channelUp'],
        };

        if (cmdMap[value]) {
            await this.executeCommand(cmdMap[value][0], 'setButton', [cmdMap[value][1]]);
        }
    });
 

 

๊ทธ๋ฆฌ๊ณ  TV ์•…์„ธ์„œ๋ฆฌ์—๋Š” ์Šคํ”ผ์ปค ์•…์„ธ์„œ๋ฆฌ๋ฅผ ํ•˜์œ„์— ๋ถ™์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ

์—ญ์‹œ ๊ทœ๊ฒฉ์— ๋งž๊ฒŒ ์Œ์†Œ๊ฑฐ ๋ฒ„ํŠผ๋„ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๋‹ค.

this.speakerService = this.accessory.getService(Service.TelevisionSpeaker);

this.speakerService.getCharacteristic(Characteristic.Mute)
    .onGet(() => false)
    .onSet(async () => {
        await this.executeCommand('statelessAudioMuteButton', 'setButton', ['muteToggle']);
    });
 

 

๋‹ค์Œ์œผ๋กœ๋Š” SetTopAccessory

์…‹ํ†ฑ๋ฐ•์Šค๋Š” ์‚ฌ์‹ค TV์™€ ๊ธฐ๋Šฅ์ด ์•„์˜ˆ ๊ฐ™์œผ๋ฏ€๋กœ, ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ๋‹ค.

์ด๋•Œ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ TV_SET_TOP_BOX๋กœ ์ง€์ •ํ•ด๋‘๋ฉด, TV์™€๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅธ ์•„์ด์ฝ˜์œผ๋กœ ๋งคํ•‘๋œ๋‹ค.

this.accessory.category = Categories.TV_SET_TOP_BOX;
 

 

์ด์ œ ์—์–ด์ปจ์„ ๋งคํ•‘ํ•ด๋ณด์ž

๊ธฐ์กด์— ์“ฐ๋˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ Fan ์•…์„ธ์„œ๋ฆฌ ํ˜•ํƒœ๋กœ ์ผœ๊ณ  ๋„๊ณ ๋งŒ ๊ฐ€๋Šฅํ–ˆ์ง€๋งŒ,

HeaterCooler๋ฅผ ์ด์šฉํ•ด์„œ ์˜จ๋„๋ฅผ ์ง๊ด€์ ์œผ๋กœ ํ™•์ธํ•˜๊ณ , ๋ชจ๋“œ์กฐ์ ˆ๋„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด ๋ณผ๊ฒƒ์ด๋‹ค.

https://developers.homebridge.io/#/service/HeaterCooler

 

Homebridge Plugin Developer Documentation

Homebridge plugin developer documentation and API reference.

developers.homebridge.io

 

์—ญ์‹œ ์•…์„ธ์„œ๋ฆฌ๋Š” HeaterCooler๋กœ ๋งคํ•‘ ํ›„,

Smartthings ๋ช…๋ น ํ‹€์— ๋งž๊ฒŒ on/off ๋ฅผ ๊ตฌํ˜„

this.service = this.accessory.getService(Service.HeaterCooler)

this.service.getCharacteristic(Characteristic.Active)
    .onGet(() => this.state.active)
    .onSet(async (value) => {
        this.state.active = value;
        const command = (value === Characteristic.Active.ACTIVE) ? 'on' : 'off';
        await this.executeCommand('switch', command);
     });
 

 

๊ทธ๋ฆฌ๊ณ , ์‚ฌ์šฉ์ค‘์ธ ์—์–ด์ปจ์—” ๋‚œ๋ฐฉ ๋ชจ๋“œ๊ฐ€ ์—†์œผ๋ฏ€๋กœ,

์—ฌ๋ฆ„์— ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ œ์Šต ์šด์ „ ๋™์ž‘์„ ๋งคํ•‘ํ•˜์—ฌ ๋ƒ‰๋ฐฉ/์ œ์Šต/์ž๋™์„ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

this.service.getCharacteristic(Characteristic.TargetHeaterCoolerState)
    .setProps({
        validValues: [
            Characteristic.TargetHeaterCoolerState.AUTO,
            Characteristic.TargetHeaterCoolerState.HEAT,
            Characteristic.TargetHeaterCoolerState.COOL
        ]
    })
    .onGet(() => this.state.mode)
    .onSet(async (value) => {
        this.state.mode = value;
        let mode = 'cool';
        if (value === Characteristic.TargetHeaterCoolerState.HEAT) mode = 'dry';
        else if (value === Characteristic.TargetHeaterCoolerState.AUTO) mode = 'auto';
        
        await this.executeCommand('airConditionerMode', 'setAirConditionerMode', [mode]);
    });
 

 

๋งˆ์ง€๋ง‰์œผ๋กœ, ์•ฝ/์ค‘/๊ฐ• ๋ฐ”๋žŒ์„ธ๊ธฐ ์กฐ์ ˆ ๋™์ž‘์„

์•…์„ธ์„œ๋ฆฌ ํ•˜์œ„์— Fan ํ˜•ํƒœ๋กœ ๋งคํ•‘ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜์˜€๋‹ค.

( ์•ฝ: 15, ์ค‘: 50, ๊ฐ•: 85 )

this.service.getCharacteristic(Characteristic.RotationSpeed)
    .setProps({ minStep: 35, minValue: 15, maxValue: 85 })
    .onGet(() => this.state.fanSpeed)
    .onSet(async (value) => {
        this.state.fanSpeed = value;
        let fanMode = 'low';
        if (value > 75) fanMode = 'high';
        else if (value > 40) fanMode = 'medium';

        await this.executeCommand('airConditionerFanMode', 'setFanMode', [fanMode]);
    });
 

 

์ด์ œ ๋งˆ์ง€๋ง‰ ์•…์„ธ์„œ๋ฆฌ์ธ ํ—ค์ดํ™ˆ ์Šค๋งˆํŠธ ํ”Œ๋Ÿฌ๊ทธ

์ƒ์ผ๋•Œ ์„ ๋ฌผ ๋ฐ›์•˜๋˜๊ฑด๋ฐ, Smartthings์—๋งŒ ๋ถ™์–ด์„œ

์ด๋ฒˆ์—์•ผ๋ง๋กœ ์—ฐ๋™ํ•ด์„œ ์ œ๋Œ€๋กœ ์จ๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

 

์•…์„ธ์„œ๋ฆฌ ๋งคํ•‘์€ Outlet

https://developers.homebridge.io/#/service/Outlet

 

Homebridge Plugin Developer Documentation

Homebridge plugin developer documentation and API reference.

developers.homebridge.io

 

Outlet ์•…์„ธ์„œ๋ฆฌ๋Š” ๋ณต์žกํ•œ ์ž‘์—…์€ ์—†์—ˆ๊ณ ,

์–˜๋Š” ๊ฐ€์ง„๊ฒƒ์ค‘ ์œ ์ผํ•˜๊ฒŒ, ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ

๊ธฐ๋ณธ๋™์ž‘๊ณผ, ์•…์„ธ์„œ๋ฆฌ ํƒ€์ผ๊ณผ ๋™๊ธฐํ™” ํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋‹ค.

this.service = this.accessory.getService(Service.Outlet);

this.service.getCharacteristic(Characteristic.On)
    .onGet(async () => {
        try {
            const response = await this.client.get('/status');
            const state = response.data.components.main.switch.switch.value;
            return state === 'on';
        } catch (e) return false;
    })
    .onSet(async (value) => {
        const command = value ? 'on' : 'off';
        await this.executeCommand('switch', command);
        this.service.updateCharacteristic(Characteristic.On, value);
    });

this.service.getCharacteristic(Characteristic.OutletInUse)
    .onGet(async () => {
        try {
            const response = await this.client.get('/status');
            return response.data.components.main.switch.switch.value === 'on';
        } catch (e) return false;
    });
 

 

์ฝ”๋“œ ์ž‘์—… ์™„๋ฃŒ.

๊ฝค ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ ธ, ๊ฒŒ์‹œ๊ธ€์—๋Š” ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ์ƒ๋žตํ–ˆ๋Š”๋ฐ,

์ด ์™ธ์— ์ƒ๋žต๋œ ๋ฉ”์„œ๋“œ๋“ค์€ ์•„๋ž˜ github ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๋˜๊ฒ ๋‹ค.

https://github.com/101Sean/homebridge-smartthings-device

 

GitHub - 101Sean/homebridge-smartthings-device: Smartthings Device ์—ฐ๋™ (Home mini IR ๋Œ€์‘)

Smartthings Device ์—ฐ๋™ (Home mini IR ๋Œ€์‘). Contribute to 101Sean/homebridge-smartthings-device development by creating an account on GitHub.

github.com

 

 

ํ…Œ์ŠคํŠธ

 

์ €๋ฒˆ ๊ฒŒ์‹œ๊ธ€์—์„œ ์ •์ƒ์ ์œผ๋กœ ํ† ํฐ์ด ๋ฐœ๊ธ‰๋˜๋Š” ๊ฑธ ํ™•์ธํ–ˆ์œผ๋‹ˆ,

์ด์   ๋””๋ฐ”์ด์Šค๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ถˆ๋Ÿฌ์™€์ง€๋Š”์ง€ ๋ด์•ผํ•œ๋‹ค.

 

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ ์šฉ ํ›„, ์ •์ƒ์ ์œผ๋กœ ๋””๋ฐ”์ด์Šค๋“ค์ด ์š”์ฒญ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

์ด์ œ ์ง์ ‘ ํ™ˆ ์•ฑ์— ๋ธŒ๋ฆฟ์ง€์™€, ์™ธ๋ถ€ ์•…์„ธ์„œ๋ฆฌ๋ฅผ ๋“ฑ๋ก ํ•˜๊ณ ,

๋ณดํ†ต, TV์™€ Set-Top์„ ๋™์‹œ์— ํ‚ค๋Š” ๋™์ž‘์„ ์ž์ฃผ ์“ฐ๋ฏ€๋กœ

ํ•ด๋‹น Scene์„ ๋งŒ๋“ค์–ด์ค˜์„œ ์›ํ„ฐ์น˜ ๋ฒ„ํŠผ์œผ๋กœ ์“ฐ๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

 

์ฃผ๋กœ ์“ฐ๋Š” ๊ธฐ๊ธฐ๋Š” Set-Top์ด๋ฏ€๋กœ, Set-Top์„ ํ…Œ์ŠคํŠธ ํ•ด๋ณด์ž

๋งคํ•‘ํ•œ ๋ฆฌ๋ชจ์ปจ์— ์•Œ๋งž๊ฒŒ ๋ชจ๋“  ๋™์ž‘์ด ๋˜๋Š”๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

https://youtube.com/shorts/Yc2hrBpyyG0?feature=share

 

 

์ด๋กœ์จ Smartthings ์ƒํƒœ๊ณ„์™€ ์• ํ”Œ ํ™ˆ์˜ ์™„์ „ํ•œ ํ†ตํ•ฉ์„ ์ด๋ค˜๋‹ค.

์•„์ง ์• ํ”Œ ํ™ˆ์•ฑ์—์„œ ์ œ๊ณตํ•˜๋Š” ์•…์„ธ์„œ๋ฆฌ ํƒ€์ž…์ด ๋‹ค์–‘ํ•˜์ง€ ์•Š์ง€๋งŒ,

์ฐจ์ฐจ ์ƒˆ๋กœ์šด ์•…์„ธ์„œ๋ฆฌ ํƒ€์ž…๋„ ์ง€์›ํ•ด์ค„๊ฑฐ๋ผ ๋ฏฟ๋Š”๋‹ค.

 

 

์›๋ณธ ๊ฒŒ์‹œ๊ธ€

https://blog.naver.com/101artspace/224122553178

 

Homebridge Smartthings ์—ฐ๋™ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ œ์ž‘, ์•…์„ธ์„œ๋ฆฌ ๋งคํ•‘ - 2

์ €๋ฒˆ ๊ฒŒ์‹œ๊ธ€์—์„œ, ํ”„๋ก์‹œ ์„œ๋ฒ„์™€ OAuth ๋ฐฉ์‹์„ ์ด์šฉํ•˜์—ฌ ์˜๊ตฌ์ ์œผ๋กœ ํ† ํฐ์„ ์ž๋™์œผ๋กœ ๋ฐœ๊ธ‰/๊ฐฑ์‹ ํ•˜์—ฌ ๊ถŒํ•œ...

blog.naver.com

๋ฐ˜์‘ํ˜•