iobroker.netatmo
Version:
ioBroker netatmo Adapter
1,371 lines (1,221 loc) • 65 kB
JavaScript
module.exports = function (myapi, myadapter) {
const api = myapi;
const adapter = myadapter;
const cleanUpInterval = adapter.config.cleanup_interval;
const EventTime = adapter.config.event_time ? adapter.config.event_time : 12;
const UnknownPersonTime = adapter.config.unknown_person_time ? adapter.config.unknown_person_time : 24;
const eventCleanUpTimer = {};
const personCleanUpTimer = {};
let knownPeople = [];
let homeIds = [];
let moduleIds = [];
let finalized = false;
let that = null;
const EventEmitterBridge = require('./eventEmitterBridge.js');
let eeB = null;
let camera_vpn;
const SDSubtypes = {
1: 'Missing SD Card',
2: 'SD Card inserted',
3: 'SD Card formatted',
4: 'Working SD Card',
5: 'Defective SD Card',
6: 'Incompatible SD Card speed',
7: 'Insufficient SD Card space'
};
const AlimentationSubtypes = {
1: 'incorrect power adapter',
2: 'correct power adapter'
};
const SirenSoundingSubtypes = {
0: 'the module stopped sounding',
1: 'the module is sounding',
};
this.init = function () {
that = this;
eeB = new EventEmitterBridge(api, adapter)
adapter.log.info(`Welcome: Registering realtime events with iot instance`);
eeB.on('alert', async data => await onSocketAlert(data));
};
this.finalize = function () {
finalized = true;
if (eeB) {
adapter.log.info('Welcome: Unregistering realtime events');
eeB.destructor();
eeB = null;
}
Object.keys(eventCleanUpTimer).forEach(id => clearInterval(eventCleanUpTimer[id]));
Object.keys(personCleanUpTimer).forEach(id => clearInterval(personCleanUpTimer[id]));
};
this.setAway = function (data, callback) {
let homes = homeIds;
data = data || {};
if (data && data.homeId) {
homes = [data.homeId];
}
if (data.personsId && !Array.isArray(data.personsId)) {
data.personsId = [data.personsId];
} else if (!data.personsId) {
data.personsId = [null];
}
let cnt = homes.length * data.personsId.length;
let errs = [];
let ress = [];
homes.forEach(aHomeId => {
data.personsId.forEach(personId => {
api.setPersonsAway(aHomeId, personId, (err, res) => {
if (err) {
adapter.log.error(`Error on setPersonsAway: ${err} ${res}`);
errs.push(err);
}
ress.push(res)
if (!--cnt && callback) {
const err = errs.length ? `Errors on setPersonsAway: ${errs.join(', ')}` : null;
callback(err, ress);
}
});
});
});
};
this.setHome = function (data, callback) {
let homes = homeIds;
if (data && data.homeId) {
homes = [data.homeId];
}
let cnt = homes.length;
let errs = [];
let ress = [];
homes.forEach(aHomeId =>
api.setPersonsHome(aHomeId, data ? data.personsId : null, (err, res) => {
if (err) {
adapter.log.error(`Error on setPersonsAway: ${err} ${res}`);
errs.push(err);
}
ress.push(res)
if (!--cnt && callback) {
const err = errs.length ? `Errors on setPersonsAway: ${errs.join(', ')}` : null;
callback(err, ress);
}
})
);
};
this.situativeUpdate = function (homeId, moduleId) {
if (finalized) return;
if (homeIds.includes(homeId) && moduleIds.includes(moduleId)) {
that.requestUpdateIndoorCamera();
}
}
this.requestUpdateIndoorCamera = function () {
return new Promise(resolve => {
api.homedataExtended({
gateway_types: ['NACamera', 'NOC'], // 'NACamDoorTag', 'NIS' ?? no cameras
}, async (err, data) => {
if (finalized) return;
if (err !== null) {
adapter.log.error(err);
} else {
const homes = data.homes;
homeIds = [];
moduleIds = [];
if (Array.isArray(homes)) {
for (let h = 0; h < homes.length; h++) {
const aHome = homes[h];
if (!aHome.modules) {
continue;
}
adapter.log.debug(`Get Welcome/Presence for Home ${h}: ${JSON.stringify(aHome)}`);
await handleHome(aHome);
const homeId = aHome.id.replace(/:/g, '-'); // formatName(aHome.name);
eventCleanUpTimer[homeId] = eventCleanUpTimer[homeId] || setInterval(() =>
cleanUpEvents(homeId), cleanUpInterval * 60 * 1000);
personCleanUpTimer[homeId] = personCleanUpTimer[homeId] || setInterval(() =>
cleanUpUnknownPersons(homeId), cleanUpInterval * 60 * 1000);
}
}
}
resolve();
});
});
};
async function onSocketAlert(data) {
if (!data || !data.device_id || (data.device_id && !moduleIds.includes(data.device_id)) || data.event_type === undefined) {
adapter.log.debug(`new alarm (welcome) IGNORE ${JSON.stringify(data)}`);
return;
}
adapter.log.debug(`new alarm (welcome) ${JSON.stringify(data)}`);
const now = new Date().toISOString();
let handledEvent = true;
if (data) {
const path = `${data.home_id.replace(/:/g, '-')}.LastEventData.`; // formatName(data.home_name) + '.LastEventData.';
const deviceEventPath = `${path}${data.device_id.replace(/:/g, '-')}.`;
const devicePath = `${data.home_id.replace(/:/g, '-')}.${data.device_id.replace(/:/g, '-')}.`;
if (data.event_type === 'person') {
for (const person of data.persons) {
let dataPath = '';
if (person.is_known) {
dataPath = 'LastKnownPersonSeen';
} else {
dataPath = 'LastUnknownPersonSeen';
}
await adapter.setStateAsync(path + dataPath, {val: now, ack: true});
// Set state first ...
if (person.is_known) {
for (const aPerson of knownPeople) {
if (aPerson.face && aPerson.face.id === person.face_id) {
await adapter.setStateAsync(`${path}LastKnownPersonName`, {val: aPerson.pseudo, ack: true});
break;
}
}
}
}
} else if (data.event_type === 'movement') {
await adapter.setStateAsync(`${path}LastMovementDetected`, {val: now, ack: true});
if (data.type) {
await adapter.setStateAsync(`${path}LastMovementType`, {val: data.type, ack: true});
} else {
await adapter.setStateAsync(`${path}LastMovementType`, {val: 'unknown', ack: true});
}
}
//add events for Presence
//type: 1 human; 2 animal; 3 vehicle
else if (data.event_type === 'human') {
await adapter.setStateAsync(`${deviceEventPath}event`, { val: true, ack: true });
await adapter.setStateAsync(`${deviceEventPath}time`, { val: now, ack: true });
await adapter.setStateAsync(`${deviceEventPath}camera_id`, { val: data.camera_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}type`, { val: 1, ack: true });
await adapter.setStateAsync(`${deviceEventPath}typename`, { val: data.event_type, ack: true });
await adapter.setStateAsync(`${deviceEventPath}message`, { val: data.message, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_id`, { val: data.snapshot_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_key`, { val: data.snapshot_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_url`, { val: data.snapshot_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_id`, { val: data.vignette_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_key`, { val: data.vignette_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_url`, { val: data.vignette_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}event_id`, { val: data.event_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}subevent_id`, { val: data.subevent_id, ack: true });
}
else if (data.event_type === 'animal') {
await adapter.setStateAsync(`${deviceEventPath}event`, { val: true, ack: true });
await adapter.setStateAsync(`${deviceEventPath}time`, { val: now, ack: true });
await adapter.setStateAsync(`${deviceEventPath}camera_id`, { val: data.camera_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}type`, { val: 2, ack: true });
await adapter.setStateAsync(`${deviceEventPath}typename`, { val: data.event_type, ack: true });
await adapter.setStateAsync(`${deviceEventPath}message`, { val: data.message, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_id`, { val: data.snapshot_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_key`, { val: data.snapshot_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_url`, { val: data.snapshot_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_id`, { val: data.vignette_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_key`, { val: data.vignette_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_url`, { val: data.vignette_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}event_id`, { val: data.event_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}subevent_id`, { val: data.subevent_id, ack: true });
}
else if (data.event_type === 'vehicle') {
await adapter.setStateAsync(`${deviceEventPath}event`, { val: true, ack: true });
await adapter.setStateAsync(`${deviceEventPath}time`, { val: now, ack: true });
await adapter.setStateAsync(`${deviceEventPath}camera_id`, { val: data.camera_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}type`, { val: 3, ack: true });
await adapter.setStateAsync(`${deviceEventPath}typename`, { val: data.event_type, ack: true });
await adapter.setStateAsync(`${deviceEventPath}message`, { val: data.message, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_id`, { val: data.snapshot_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_key`, { val: data.snapshot_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_url`, { val: data.snapshot_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_id`, { val: data.vignette_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_key`, { val: data.vignette_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_url`, { val: data.vignette_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}event_id`, { val: data.event_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}subevent_id`, { val: data.subevent_id, ack: true });
}
else if (data.snapshot_id || data.vignette_id) {
await adapter.setStateAsync(`${deviceEventPath}event`, { val: false, ack: true });
await adapter.setStateAsync(`${deviceEventPath}time`, { val: null, ack: true });
await adapter.setStateAsync(`${deviceEventPath}camera_id`, { val: null, ack: true });
await adapter.setStateAsync(`${deviceEventPath}type`, { val: null, ack: true });
await adapter.setStateAsync(`${deviceEventPath}typename`, { val: null, ack: true });
await adapter.setStateAsync(`${deviceEventPath}message`, { val: null, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_id`, { val: data.snapshot_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_key`, { val: data.snapshot_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}snapshot_url`, { val: data.snapshot_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_id`, { val: data.vignette_id, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_key`, { val: data.vignette_key, ack: true });
await adapter.setStateAsync(`${deviceEventPath}vignette_url`, { val: data.vignette_url, ack: true });
await adapter.setStateAsync(`${deviceEventPath}event_id`, { val: null, ack: true });
await adapter.setStateAsync(`${deviceEventPath}subevent_id`, { val: null, ack: true });
} else {
handledEvent = false;
that.requestUpdateIndoorCamera();
}
if (handledEvent) {
await adapter.setStateAsync(`${path}LastPushType`, {val: data.push_type, ack: true});
await adapter.setStateAsync(`${path}LastEventType`, {val: data.event_type, ack: true});
await adapter.setStateAsync(`${path}LastEventDeviceId`, {val: data.device_id, ack: true});
await adapter.setStateAsync(`${path}LastEventId`, {val: data.event_id, ack: true});
await adapter.setStateAsync(`${path}LastEvent`, {val: now, ack: true});
/*await adapter.setStateAsync(`${devicePath}${data.event_type}.LastEvent`, {
val: now,
ack: true
});
await adapter.setStateAsync(`${devicePath}${data.event_type}.LastEventId`, {
val: data.event_id,
ack: true
});*/
}
}
}
/*
function formatName(aHomeName) {
return aHomeName.replace(/ /g, '-').replace(/---/g, '-').replace(/--/g, '-').replace(adapter.FORBIDDEN_CHARS, '_').replace(/\s|\./g, '_');
}
*/
async function handleHome(aHome) {
const homeId = aHome.id.replace(/:/g, '-'); //formatName(aHome.name);
const fullPath = homeId;
homeIds.push(aHome.id);
// Join HomeID
if (eeB) {
eeB.joinHome(aHome.id);
}
await adapter.extendOrSetObjectNotExistsAsync(homeId, {
type: 'folder',
common: {
name: aHome.name || aHome.id,
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastMovementDetected`, {
type: 'state',
common: {
name: 'LastMovementDetected',
type: 'string',
role: 'value.date',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastKnownPersonSeen`, {
type: 'state',
common: {
name: 'LastKnownPersonSeen',
type: 'string',
role: 'value.date',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastMovementType`, {
type: 'state',
common: {
name: 'LastMovementType',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastEventType`, {
type: 'state',
common: {
name: 'LastEventTypes',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastEventDeviceId`, {
type: 'state',
common: {
name: 'LastEventDeviceId',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastPushType`, {
type: 'state',
common: {
name: 'LastPushType',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastEventId`, {
type: 'state',
common: {
name: 'LastEventId',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastEvent`, {
type: 'state',
common: {
name: 'LastEvent',
type: 'string',
role: 'value.date',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastKnownPersonName`, {
type: 'state',
common: {
name: 'LastKnownPersonName',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData.LastUnknownPersonSeen`, {
type: 'state',
common: {
name: 'LastUnknownPersonSeen',
type: 'string',
role: 'value.date',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${homeId}.LastEventData`, {
type: 'channel',
common: {
name: 'LastEventData',
}
});
if (aHome.modules) {
for (const aCamera of aHome.modules) {
if (aCamera.id) {
moduleIds.push(aCamera.id);
await handleCamera(aCamera, aHome);
}
}
}
if (aHome.persons) {
knownPeople = [];
for (const aPerson of aHome.persons) {
await handlePerson(aPerson, homeId);
}
}
if (aHome.events) {
let latestEventDate = 0;
let latestEvent = null;
for (const aEvent of aHome.events) {
const eventDate = aEvent.time * 1000;
adapter.log.debug(`Handle Event: ${JSON.stringify(aEvent)}`);
await handleEvent(aEvent, homeId, aHome.modules);
if (eventDate > latestEventDate) {
latestEventDate = eventDate;
latestEvent = aEvent;
}
}
if (latestEvent) {
await adapter.setStateAsync(`${homeId}.LastEventData.LastEventId`, {val: latestEvent.id, ack: true});
}
}
}
async function handleCamera(aCamera, aHome) {
const aParent = aHome.id.replace(/:/g, '-'); // formatName(aHome.name);
const fullPath = `${aParent}.${aCamera.id.replace(/:/g, '-')}`;
const infoPath = `${fullPath}.info`;
const livePath = `${fullPath}.live`;
await adapter.extendOrSetObjectNotExistsAsync(fullPath, {
type: 'device',
common: {
name: aCamera.name || aCamera.id,
},
native: {
id: aCamera.id,
type: aCamera.type
}
});
await adapter.extendOrSetObjectNotExistsAsync(infoPath, {
type: 'channel',
common: {
name: `${aCamera.name || aCamera.id} Info`,
},
native: {
}
});
await adapter.extendOrSetObjectNotExistsAsync(livePath, {
type: 'channel',
common: {
name: `${aCamera.name || aCamera.id} Live`,
},
native: {
}
});
if (aCamera.id) {
await adapter.extendOrSetObjectNotExistsAsync(`${infoPath}.id`, {
type: 'state',
common: {
name: 'Camera ID',
type: 'string',
read: true,
write: false
}
});
await adapter.setStateAsync(`${infoPath}.id`, {val: aCamera.id, ack: true});
}
if (aCamera.monitoring) {
await adapter.extendOrSetObjectNotExistsAsync(`${infoPath}.monitoring`, {
type: 'state',
common: {
name: 'Monitoring State (on/off)',
type: 'string',
states: {'on': 'on', 'off': 'off'},
role: 'state',
read: true,
write: true
},
native: {
homeId: aHome.id,
moduleId: aCamera.id,
field: 'monitoring'
}
});
await adapter.setStateAsync(`${infoPath}.monitoring`, {val: aCamera.monitoring, ack: true});
}
if (aCamera.floodlight) {
await adapter.extendOrSetObjectNotExistsAsync(`${infoPath}.floodlight`, {
type: 'state',
common: {
name: 'Floodlight State (on/off/auto)',
type: 'string',
states: {'on': 'on', 'off': 'off', 'auto': 'auto'},
role: 'state',
read: true,
write: true
},
native: {
homeId: aHome.id,
moduleId: aCamera.id,
field: 'floodlight'
}
});
await adapter.setStateAsync(`${infoPath}.floodlight`, {val: aCamera.floodlight, ack: true});
}
if (aCamera.sd_status) {
await adapter.extendOrSetObjectNotExistsAsync(`${infoPath}.sd_status`, {
type: 'state',
common: {
name: 'SD card State (on/off)',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${infoPath}.sd_status`, {val: SDSubtypes[aCamera.sd_status], ack: true});
}
if (aCamera.alim_status) {
await adapter.extendOrSetObjectNotExistsAsync(`${infoPath}.alim_status`, {
type: 'state',
common: {
name: 'Power Supply State (on/off)',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${infoPath}.alim_status`, {val: AlimentationSubtypes[aCamera.alim_status], ack: true});
}
if (aCamera.name) {
await adapter.extendOrSetObjectNotExistsAsync(`${infoPath}.name`, {
type: 'state',
common: {
name: 'Camera name',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${infoPath}.name`, {val: aCamera.name, ack: true});
}
if (aCamera.vpn_url) {
await adapter.extendOrSetObjectNotExistsAsync(`${livePath}.picture`, {
type: 'state',
common: {
name: 'Live camera picture URL',
type: 'string',
role: 'url.cam',
read: true,
write: false
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${livePath}.stream`, {
type: 'state',
common: {
name: 'Live camera picture URL',
type: 'string',
role: 'url.cam',
read: true,
write: false
}
});
camera_vpn = aCamera.vpn_url;
await adapter.setStateAsync(`${livePath}.picture`, {val: `${aCamera.vpn_url}/live/snapshot_720.jpg`, ack: true});
await adapter.setStateAsync(`${livePath}.stream`, {
val: `${aCamera.vpn_url}${aCamera.is_local ? '/live/index_local.m3u8' : '/live/index.m3u8'}`,
ack: true
});
}
const deviceEventPath = `${aParent}.LastEventData.${aCamera.id.replace(/:/g, '-')}.`;
// create sub-states for Doorbell
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath.substring(0, deviceEventPath.length - 1)}`, {
type: 'channel',
common: {
name: 'Events',
},
native: {
}
});
// create sub-states for Presence
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}event`, {
type: 'state',
common: {
name: 'event',
type: 'boolean',
role: 'indicator',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}time`, {
type: 'state',
common: {
name: 'time',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}camera_id`, {
type: 'state',
common: {
name: 'camera_id',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}type`, {
type: 'state',
common: {
name: 'type',
type: 'number',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}typename`, {
type: 'state',
common: {
name: 'typename',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}message`, {
type: 'state',
common: {
name: 'message',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}snapshot_id`, {
type: 'state',
common: {
name: 'snapshot_id',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}snapshot_key`, {
type: 'state',
common: {
name: 'snapshot_key',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}snapshot_url`, {
type: 'state',
common: {
name: 'snapshot_url',
type: 'string',
role: 'url.cam',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}vignette_id`, {
type: 'state',
common: {
name: 'vignette_id',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}vignette_key`, {
type: 'state',
common: {
name: 'vignette_key',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}vignette_url`, {
type: 'state',
common: {
name: 'vignette_url',
type: 'string',
role: 'url',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}event_id`, {
type: 'state',
common: {
name: 'event_id',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
await adapter.extendOrSetObjectNotExistsAsync(`${deviceEventPath}subevent_id`, {
type: 'state',
common: {
name: 'subevent_id',
type: 'string',
role: 'state',
read: true,
write: false
},
native: {
id: aHome.id
}
});
// Initialize Camera Place
if (aHome.place) {
await handlePlace(aHome.place, fullPath);
}
}
async function handlePlace(aPlace, aParent) {
const fullPath = `${aParent}.place`;
await adapter.extendOrSetObjectNotExistsAsync(fullPath, {
type: 'channel',
common: {
name: 'place',
}
});
if (aPlace.city) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.city`, {
type: 'state',
common: {
name: 'city',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.city`, {val: aPlace.city, ack: true});
}
if (aPlace.country) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.country`, {
type: 'state',
common: {
name: 'country',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.country`, {val: aPlace.country, ack: true});
}
if (aPlace.timezone) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.timezone`, {
type: 'state',
common: {
name: 'timezone',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.timezone`, {val: aPlace.timezone, ack: true});
}
}
/*
function formatPersonName(aPersonName) {
return aPersonName.replace(/ /g, '-').replace(/---/g, '-').replace(/--/g, '-').replaceAll('ß', 'ss').replace(adapter.FORBIDDEN_CHARS, '_').replace(/\s|\./g, '_');
}
*/
async function handlePerson(aPerson, aParent) {
let personId = aPerson.id;
let bKnown = true;
let cleanupDate = new Date().getTime();
if (!aPerson.pseudo) {
bKnown = false;
cleanupDate -= UnknownPersonTime * 60 * 60 * 1000;
}
const personDate = aPerson.last_seen ? aPerson.last_seen * 1000 : cleanupDate;
adapter.log.debug(`handlePerson: ${personId} ${aPerson.pseudo} ${aPerson.last_seen} ${personDate} ${cleanupDate}`);
if (bKnown || personDate > cleanupDate) {
let fullPath = `${aParent}.Persons`;
await adapter.extendOrSetObjectNotExistsAsync(fullPath, {
type: 'folder',
common: {
name: 'Persons',
}
});
if (bKnown) {
fullPath += '.Known';
knownPeople.push(aPerson);
}
else {
fullPath += '.Unknown';
}
await adapter.extendOrSetObjectNotExistsAsync(fullPath, {
type: 'folder',
common: {
name: fullPath,
}
});
fullPath += `.${personId}`;
await adapter.extendOrSetObjectNotExistsAsync(fullPath, {
type: 'channel',
common: {
name: aPerson.pseudo || fullPath,
},
native: {
id: personId
}
});
if (aPerson.id) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.id`, {
type: 'state',
common: {
name: 'Person ID',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.id`, {val: aPerson.id, ack: true});
}
if (aPerson.pseudo) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.name`, {
type: 'state',
common: {
name: 'Person Name',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.name`, {val: aPerson.pseudo, ack: true});
}
if (aPerson.out_of_sight !== undefined) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.out_of_sight`, {
type: 'state',
common: {
name: 'Person out of sight (true/false)',
type: 'boolean',
role: 'indicator',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.out_of_sight`, {val: aPerson.out_of_sight, ack: true});
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.atHome`, {
type: 'state',
common: {
name: 'Person at home (true/false)',
type: 'boolean',
role: 'indicator',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.atHome`, {val: !aPerson.out_of_sight, ack: true});
}
if (aPerson.last_seen) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.last_seen`, {
type: 'state',
common: {
name: 'Last seen',
type: 'number',
role: 'value.date',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.last_seen`, {
val: aPerson.last_seen * 1000,
ack: true
});
}
if (aPerson.face !== undefined) {
await handleFace(aPerson.face, fullPath);
}
}
}
async function handleFace(aFace, aParent) {
const fullPath = aParent;
if (aFace.id) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.face_id`, {
type: 'state',
common: {
name: 'Face ID',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.face_id`, {val: aFace.id, ack: true});
}
if (aFace.key) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.face_key`, {
type: 'state',
common: {
name: 'Face Key',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.face_key`, {val: aFace.key, ack: true});
}
if (aFace.id && aFace.key) {
const imageUrl = `https://api.netatmo.com/api/getcamerapicture?image_id=${aFace.id}&key=${aFace.key}`;
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.face_url`, {
type: 'state',
common: {
name: 'Face Url',
type: 'string',
role: 'url',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.face_url`, {
val: imageUrl,
ack: true
});
}
if (aFace.version) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.face_version`, {
type: 'state',
common: {
name: 'Version',
type: 'number',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.face_version`, {val: aFace.version, ack: true});
}
}
async function handleEvent(aEvent, aParent, aCameraList) {
const cleanupDate = new Date().getTime() - EventTime * 60 * 60 * 1000;
const eventDate = aEvent.time ? aEvent.time * 1000 : cleanupDate;
if (cleanupDate < eventDate) {
let fullPath = `${aParent}.Events`;
let camera = null;
await adapter.extendOrSetObjectNotExistsAsync(fullPath, {
type: 'folder',
common: {
name: 'Events',
}
});
fullPath += `.${aEvent.id}`;
await adapter.extendOrSetObjectNotExistsAsync(fullPath, {
type: 'channel',
common: {
name: aEvent.id,
},
native: {
id: `Events.${aEvent.id}`
}
});
if (aEvent.id) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.id`, {
type: 'state',
common: {
name: 'Event ID',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.id`, {val: aEvent.id, ack: true});
}
if (aEvent.message) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.message`, {
type: 'state',
common: {
name: 'Message',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.message`, {val: aEvent.message, ack: true});
}
if (aEvent.type) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.type`, {
type: 'state',
common: {
name: 'Type',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.type`, {val: aEvent.type, ack: true});
}
if (aEvent.category) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.category`, {
type: 'state',
common: {
name: 'Category',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.category`, {val: aEvent.category, ack: true});
}
if (aEvent.time) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.time`, {
type: 'state',
common: {
name: 'Time',
type: 'number',
role: 'value.date',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.time`, {
val: aEvent.time * 1000,
ack: true
});
}
if (aEvent.person_id) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.person_id`, {
type: 'state',
common: {
name: 'Person ID',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.person_id`, {val: aEvent.person_id, ack: true});
}
if (aEvent.camera_id) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.camera_id`, {
type: 'state',
common: {
name: 'Camera ID',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.camera_id`, {val: aEvent.camera_id, ack: true});
aCameraList.forEach(function (aCamera) {
if (aCamera.id === aEvent.camera_id)
camera = aCamera;
});
}
if (aEvent.sub_type) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.sub_type`, {
type: 'state',
common: {
name: 'Sub Type',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.sub_type`, {val: aEvent.sub_type, ack: true});
}
if (aEvent.video_id) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.video_id`, {
type: 'state',
common: {
name: 'Video ID',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.video_id`, {val: aEvent.video_id, ack: true});
if (camera) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.video_url`, {
type: 'state',
common: {
name: 'Video URL',
type: 'string',
role: 'url',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.video_url`, {
val: `${camera.vpn_url}/vod/${aEvent.video_id}${camera.is_local ? '/index_local.m3u8' : '/index.m3u8'}`,
ack: true
});
}
}
if (aEvent.video_status) {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.video_status`, {
type: 'state',
common: {
name: 'Video Status',
type: 'string',
role: 'state',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.video_status`, {val: aEvent.video_status, ack: true});
}
if (aEvent.is_arrival !== undefined && aEvent.is_arrival !== '') {
await adapter.extendOrSetObjectNotExistsAsync(`${fullPath}.is_arrival`, {
type: 'state',
common: {
name: 'Is Arrival',
type: 'boolean',
role: 'indicator',
read: true,
write: false
}
});
await adapter.setStateAsync(`${fullPath}.is_arrival`, {val: aEvent.is_arrival, ack: true});
}
if (aEvent.snapshot) {
await handleSnapshot(aEvent.snapshot, fullPath);
}
if (aEvent.vignette) {
await handleVignette(aEvent.vignette, fullPath);
}
//add event history for Presence with subevent tree
if (aEvent.type === 'outdoor') {
let counter = 0;
await adapter.extendOrSetObjectNotExis