Skip to content

Commit

Permalink
feat(device): add xiaomi aqara magic cube
Browse files Browse the repository at this point in the history
  • Loading branch information
Neonox31 committed Aug 6, 2018
1 parent 8bc3365 commit 1a70e58
Show file tree
Hide file tree
Showing 5 changed files with 509 additions and 5 deletions.
13 changes: 9 additions & 4 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,17 @@ export interface CommonReedSwitchPayload {

export interface CommonRemotePayload {
shake?: boolean
slide?: boolean
rotation?: {
axis?: CommonAxis
flip?: {
degrees?: number
fromFace?: number
toFace?: number
}
push?: {
face?: number
}
doubleTap?: {
face?: number
}
tap?: boolean
}

export interface CommonLightPayload {
Expand Down
6 changes: 5 additions & 1 deletion src/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { ZiGate } from './zigate'
import debug from './debug'
import { ZGXiaomiAqaraDoorSensorDevice } from './devices/xiaomi/aqara-door-sensor'
import { ZGXiaomiAqaraWeatherSensorDevice } from './devices/xiaomi/aqara-weather-sensor'
import { ZGXiaomiAqaraMagicCubeDevice } from './devices/xiaomi/aqara-magic-cube/aqara-magic-cube'

export interface ZGDevice {}

export enum ZGDeviceType {
XiaomiAqaraButton,
XiaomiAqaraDoorSensor,
XiaomiAqaraWeatherSensor
XiaomiAqaraWeatherSensor,
XiaomiAqaraMagicCube
}

export function createZGDevice(
Expand All @@ -25,6 +27,8 @@ export function createZGDevice(
return new ZGXiaomiAqaraDoorSensorDevice(zigate, shortAddress)
case ZGDeviceType.XiaomiAqaraWeatherSensor:
return new ZGXiaomiAqaraWeatherSensorDevice(zigate, shortAddress)
case ZGDeviceType.XiaomiAqaraMagicCube:
return new ZGXiaomiAqaraMagicCubeDevice(zigate, shortAddress)
default:
throw new Error(`Unsupported device type : ${deviceType}`)
}
Expand Down
162 changes: 162 additions & 0 deletions src/devices/xiaomi/aqara-magic-cube/aqara-magic-cube.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { Observable } from 'rxjs'
import { filter, map, tap } from 'rxjs/operators'
import { ZGDevice } from '../../../device'
import { ZiGate } from '../../../zigate'
import { asAttributeReportMessage, ZGMessageCode } from '../../../message'
import { AttributeType, ZGAttributeReportMessage } from '../../../messages/attribute-report'
import debug from '../../../debug'
import { asCommonBatteryPayload, BatteryType } from '../utils/battery'
import { CommonBatteryPayload, CommonRemotePayload } from '../../../common'
import oneFaceFlipAttributes from './flip-attributes'

const isGestureMessage = (msg: ZGAttributeReportMessage) => {
return (
msg.getPayload().srcEndpoint === 0x2 &&
msg.getPayload().clusterId === 0x0012 &&
msg.getPayload().attributeId === 0x0055 &&
msg.getPayload().attributeType === AttributeType.UInt16
)
}

const getOneFaceFlipAttribute = (attribute: number) => {
return oneFaceFlipAttributes.find(attr => attr.value === attribute)
}

const isOneFaceFlipMessage = (msg: ZGAttributeReportMessage) => {
return getOneFaceFlipAttribute(msg.getPayload().attributeData as number) !== undefined
}

const isTwoFacesFlipMessage = (msg: ZGAttributeReportMessage) => {
return (
(msg.getPayload().attributeData as number) >= 0x0080 &&
(msg.getPayload().attributeData as number) <= 0x0085
)
}

const shakeMessages = (msg: ZGAttributeReportMessage) => {
return isGestureMessage(msg) && msg.getPayload().attributeData === 0x0000
}

const flipMessages = (msg: ZGAttributeReportMessage) => {
return isGestureMessage(msg) && (isOneFaceFlipMessage(msg) || isTwoFacesFlipMessage(msg))
}

const pushMessages = (msg: ZGAttributeReportMessage) => {
return (
isGestureMessage(msg) &&
(msg.getPayload().attributeData as number) >= 0x0100 &&
(msg.getPayload().attributeData as number) <= 0x0105
)
}

const doubleTapMessages = (msg: ZGAttributeReportMessage) => {
return isGestureMessage(msg) && msg.getPayload().attributeData === 0x0204
}

const batteryMessages = (msg: ZGAttributeReportMessage) => {
return (
msg.getPayload().srcEndpoint === 0x1 &&
msg.getPayload().clusterId === 0x0 &&
msg.getPayload().attributeId === 0xff01 &&
msg.getPayload().attributeSize === 0x002a
)
}

const asCommonFlipRemotePayload = (msg: ZGAttributeReportMessage): CommonRemotePayload => {
const oneFaceFlipAttr = getOneFaceFlipAttribute(msg.getPayload().attributeData as number)

if (oneFaceFlipAttr !== undefined) {
return {
flip: {
degrees: 90,
fromFace: oneFaceFlipAttr.fromFace,
toFace: oneFaceFlipAttr.toFace
}
}
}

return {
flip: {
degrees: 180,
toFace: (msg.getPayload().attributeData as number) - 0x0080
}
}
}

const asCommonPushRemotePayload = (msg: ZGAttributeReportMessage): CommonRemotePayload => {
return {
push: {
face: (msg.getPayload().attributeData as number) - 0x0100
}
}
}

const asCommonDoubleTapRemotePayload = (msg: ZGAttributeReportMessage): CommonRemotePayload => {
return {
doubleTap: {
face: (msg.getPayload().attributeData as number) - 0x0200
}
}
}

export class ZGXiaomiAqaraMagicCubeDevice implements ZGDevice {
label = 'xiaomi-aqara-magic-cube'
shortAddress: string

messages$: Observable<ZGAttributeReportMessage>
shake$: Observable<CommonRemotePayload>
push$: Observable<CommonRemotePayload>
flip$: Observable<CommonRemotePayload>
doubleTap$: Observable<CommonRemotePayload>
battery$: Observable<CommonBatteryPayload>

constructor(zigate: ZiGate, shortAddress: string) {
this.shortAddress = shortAddress

this.messages$ = zigate.messages$.pipe(
filter(msg => msg.getCode() === ZGMessageCode.AttributeReport),
map(asAttributeReportMessage),
filter(msg => msg.getPayload().srcAddress === this.shortAddress)
)

this.shake$ = this.messages$.pipe(
filter(shakeMessages),
map(_ => ({ shake: true })),
tap((payload: CommonRemotePayload) =>
debug(`device:${this.label}:${this.shortAddress}:remote`)(payload)
)
)

this.push$ = this.messages$.pipe(
filter(pushMessages),
map(asCommonPushRemotePayload),
tap((payload: CommonRemotePayload) =>
debug(`device:${this.label}:${this.shortAddress}:remote`)(payload)
)
)

this.flip$ = this.messages$.pipe(
filter(flipMessages),
map(asCommonFlipRemotePayload),
tap((payload: CommonRemotePayload) =>
debug(`device:${this.label}:${this.shortAddress}:remote`)(payload)
)
)

this.doubleTap$ = this.messages$.pipe(
filter(doubleTapMessages),
map(asCommonDoubleTapRemotePayload),
tap((payload: CommonRemotePayload) =>
debug(`device:${this.label}:${this.shortAddress}:remote`)(payload)
)
)

this.battery$ = this.messages$.pipe(
filter(batteryMessages),
map(msg => asCommonBatteryPayload(msg, BatteryType.CR1632)),
tap((payload: CommonBatteryPayload) =>
debug(`device:${this.label}:${this.shortAddress}:battery`)(payload)
)
)
}
}
28 changes: 28 additions & 0 deletions src/devices/xiaomi/aqara-magic-cube/flip-attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const oneFaceFlipAttributes = [
{ value: 0x41, fromFace: 0, toFace: 1 },
{ value: 0x42, fromFace: 0, toFace: 2 },
{ value: 0x44, fromFace: 0, toFace: 4 },
{ value: 0x45, fromFace: 0, toFace: 5 },
{ value: 0x48, fromFace: 1, toFace: 0 },
{ value: 0x4a, fromFace: 1, toFace: 2 },
{ value: 0x4b, fromFace: 1, toFace: 3 },
{ value: 0x4d, fromFace: 1, toFace: 5 },
{ value: 0x50, fromFace: 2, toFace: 0 },
{ value: 0x51, fromFace: 2, toFace: 1 },
{ value: 0x53, fromFace: 2, toFace: 3 },
{ value: 0x54, fromFace: 2, toFace: 4 },
{ value: 0x59, fromFace: 3, toFace: 1 },
{ value: 0x5a, fromFace: 3, toFace: 2 },
{ value: 0x5c, fromFace: 3, toFace: 4 },
{ value: 0x5d, fromFace: 3, toFace: 5 },
{ value: 0x60, fromFace: 4, toFace: 0 },
{ value: 0x62, fromFace: 4, toFace: 2 },
{ value: 0x63, fromFace: 4, toFace: 3 },
{ value: 0x65, fromFace: 4, toFace: 5 },
{ value: 0x68, fromFace: 5, toFace: 0 },
{ value: 0x69, fromFace: 5, toFace: 1 },
{ value: 0x6b, fromFace: 5, toFace: 3 },
{ value: 0x6c, fromFace: 5, toFace: 4 }
]

export default oneFaceFlipAttributes
Loading

0 comments on commit 1a70e58

Please sign in to comment.