Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ interface of a device, under *Settings -> Device info -> Device ID*.
#### Shelly 2.5 configurations
* `"type"` - in roller mode, the device can be identified as either `"door"`,
`"garageDoorOpener"`, `"window"` or `"windowCovering"` (default).
* For detached mode, you can use a `type` of `"detachedContactSensor"`; in
this configuration, you won't be able to control the relay through HomeKit
(you can still control it through the Shelly directly). What will be
exposed instead is the status of the connected light switch.
* You can also specify certain types on a per-channel basis: `skip` will
cause Homebridge to not configure that channel at all, and other options
(like `detachedContactSensor`, `switch`, `outlet`, etc.) make it possible
to have a heterogenous mixture of types and configurations attached to a
single device.

#### Shelly RGBW2 configurations
* `"colorMode"` - set to `"rgbw"` (default) to have HomeKit control all four
Expand All @@ -146,6 +155,7 @@ interface of a device, under *Settings -> Device info -> Device ID*.
{ "id": "6A78BB", "colorMode": "rgb" },
{ "id": "AD2214", "name": "My Device" },
{ "id": "1D56AF", "type": "outlet" }
{ "id": "921A84CFBBA7", "type": "outlet", "channels": [{"type": "skip"}, {}] }
],
"admin": {
"enabled": true,
Expand All @@ -160,7 +170,8 @@ If you have a Shelly device that is not yet supported by this plugin you can
help adding support for it by following these steps:

1. Run `$ homebridge-shelly describe <ip-address>` with the IP address of the
Shelly device.
Shelly device. (If you are using HTTP auth, you'll need to edit the script
to hardcode those values.)
2. Create [a new issue](https://github.com/alexryd/homebridge-shelly/issues)
and post the output from the previous command.

Expand Down
9 changes: 7 additions & 2 deletions abilities/contact-sensor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ module.exports = homebridge => {
* @param {string} detectedProperty - The device property used to indicate
* whether contact has been detected.
*/
constructor(detectedProperty) {
constructor(detectedProperty, invert=true) {
super(
Service.ContactSensor,
Characteristic.ContactSensorState,
detectedProperty
)

this.invert = invert
}

_valueToHomeKit(value) {
const CSS = Characteristic.ContactSensorState
return !value ? CSS.CONTACT_DETECTED : CSS.CONTACT_NOT_DETECTED
if ( this.invert ) {
return !value ? CSS.CONTACT_DETECTED : CSS.CONTACT_NOT_DETECTED
}
return value ? CSS.CONTACT_DETECTED : CSS.CONTACT_NOT_DETECTED
}
}

Expand Down
12 changes: 12 additions & 0 deletions accessories/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports = homebridge => {
ShellyFloodAccessory,
ShellyGasSmokeSensorAccessory,
ShellyHTAccessory,
ShellyInputContactSensorAccessory,
ShellyRelayContactSensorAccessory,
ShellyRelayMotionSensorAccessory,
ShellyRelayOccupancySensorAccessory,
Expand Down Expand Up @@ -136,10 +137,12 @@ module.exports = homebridge => {
}

_createAccessory(accessoryType, index, config, log) {
const util=require('util')
const powerMeterIndex = this.numberOfPowerMeters > 0
? Math.min(index, this.numberOfPowerMeters - 1)
: false

log.debug(`Creating accessory ${accessoryType} at ${index} with ${util.inspect(config)} ${powerMeterIndex === false ? 'without power tracking' : `with power at ${powerMeterIndex}`}.`)
return this._createAccessoryForRelay(
accessoryType,
index,
Expand All @@ -156,6 +159,8 @@ module.exports = homebridge => {
_createAccessoryForRelay(accessoryType, ...opts) {
if (accessoryType === 'contactSensor') {
return new ShellyRelayContactSensorAccessory(this.device, ...opts)
} else if (accessoryType === 'detachedContactSensor') {
return new ShellyInputContactSensorAccessory(this.device, ...opts)
} else if (accessoryType === 'motionSensor') {
return new ShellyRelayMotionSensorAccessory(this.device, ...opts)
} else if (accessoryType === 'occupancySensor') {
Expand Down Expand Up @@ -453,6 +458,8 @@ module.exports = homebridge => {
}

_createAccessory(accessoryType, index, config, log) {
const util=require('util')
log.debug(`Shelly2Factory _createAccessory(${accessoryType}, ${index}, ${util.inspect(config)})`)
if (this.device.mode === 'roller') {
if (accessoryType === 'door') {
return new Shelly2DoorAccessory(this.device, index, config, log)
Expand All @@ -474,6 +481,11 @@ module.exports = homebridge => {
)
}

accessoryType = config?.channels?.[index]?.type ?? accessoryType
log.debug(`Shelly2Factory creating accessory ${this.device.id}[${index}] with type ${accessoryType} using config ${util.inspect(config)} ${this.device.settings ? `with device settings of ${util.inspect(this.device.settings.relays[index])}` : 'without settings loaded from the device yet'}.`)
if ( accessoryType == 'skip' ) {
log.info(`Skipping setup on ${this.device.id}[${index}], by config request.`)
}
return super._createAccessory(accessoryType, index, config, log)
}
}
Expand Down
17 changes: 17 additions & 0 deletions accessories/sensors.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ module.exports = homebridge => {
}
}

class ShellyInputContactSensorAccessory extends ShellyAccessory {
constructor(device, index, config, log, powerMeterIndex = false) {
super('contactSensor', device, index, config, log)

this.abilities.push(new ContactSensorAbility('input' + index, false))

if (powerMeterIndex !== false) {
this.abilities.push(new PowerMeterAbility('power' + powerMeterIndex))
}
}

get category() {
return Accessory.Categories.SENSOR
}
}

class ShellyRelayContactSensorAccessory extends ShellyAccessory {
constructor(device, index, config, log, powerMeterIndex = false) {
super('contactSensor', device, index, config, log)
Expand Down Expand Up @@ -158,6 +174,7 @@ module.exports = homebridge => {
ShellyFloodAccessory,
ShellyGasSmokeSensorAccessory,
ShellyHTAccessory,
ShellyInputContactSensorAccessory,
ShellyRelayContactSensorAccessory,
ShellyRelayMotionSensorAccessory,
ShellyRelayOccupancySensorAccessory,
Expand Down
1 change: 1 addition & 0 deletions bin/homebridge-shelly
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const describe = async host => {
console.log(v('Type', device.type))
console.log(v('CoAP description', JSON.stringify(description.payload)))
console.log(v('CoAP status', JSON.stringify(status.payload)))
//device.setAuthCredentials('username', 'password') // hardcode Basic auth here, if used
console.log(v('HTTP Settings:', JSON.stringify(await device.getSettings())))
console.log(v('HTTP Status:', JSON.stringify(await device.getStatus())))
}
Expand Down
5 changes: 3 additions & 2 deletions platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,10 @@ module.exports = homebridge => {

try {
if (!this.addDevice(device)) {
this.log.info('Unknown device, so skipping it')
this.log.info(`Unknown device ${device}, so skipping it`)
}
} catch (e) {
this.log.error('Failed to add device')
this.log.error(`Failed to add device ${device} [${device.type}], ${device.id}@${device.host} due to ${e}`)
if (e.stack) {
this.log.error(e.stack)
}
Expand Down Expand Up @@ -205,6 +205,7 @@ module.exports = homebridge => {
removeDevice(device) {
const deviceWrapper = this.deviceWrappers.get(device)
if (!deviceWrapper) {
this.log.info(`No deviceWrapper found for ${device.id} in removeDevice().`)
return
}

Expand Down